Flutter - Remove Border/Underline of TextField/TextFormField

This tutorial shows you how to display a TextField or TextFormField in Flutter without a border or underline.

Text fields are usually used as an interface that accepts user input from the keyboard. In Flutter, you can use the TextField or TextFormField widget to create such an interface. By default, Flutter renders an underline at the bottom. If you don't want the underline to appear, read the examples in this tutorial.

Remove Border/Underline of a Specific TextField/TextFormField

The TextField and TextFormField have an argument named decoration that allows you to pass a custom InputDecoration. The InputDecoration itself can be used to customize how the text field looks. That includes specifying the border.

The InputDecoration has some arguments for setting the border depending on the current state. For example, you can set a different style when the field is in the focused state. Below is the list of the arguments

  • enabledBorder: used when enabled and not showing an error.
  • disabledBorder: used when disabled and not showing an error.
  • focusedBorder: used when in focus and not showing an error.
  • errorBorder: used when not in focus and showing an error.
  • focusedErrorBorder: used when in focus and showing an error.
  • border: shape of the border to draw around the decoration's container when the corresponding argument above (based on the current state) is not defined.

Flutter will use the corresponding argument based on the current state of the field. Therefore, it's also possible to not display the border only in certain states. All of the arguments have the same type which is InputBorder?. If the corresponding argument for a state doesn't exist, the one defined by the border argument will be used. The default value is UnderlineInputBorder, which draws a horizontal line at the bottom. That's why you'll see an underline even if you don't set it explicitly.

Using InputBorder.none

To remove the underline, you have to pass an InputBorder that doesn't have any border line. Assuming there is no state-specific style that has been set, you can set it to have no border by passing InputBorder.none to the border argument.

  TextField(
    decoration: const InputDecoration(
      border: InputBorder.none,
      labelText: 'Name',
    ),
    // other arguments
  )

Output:

Flutter - Text Field - InputBorder.none

If you have set the border argument but the border is still visible, probably it's from the state-specific style defined in the theme. For example, if you define enabledBorder in the theme, it will be used for all text fields which do not have its own enabledBorder style. Therefore, you may also need to specify enabledBorder to use InputBorder.none. The same also applies for other state-specific arguments.

  TextField(
    decoration: const InputDecoration(
      enabledBorder: InputBorder.none,
      border: InputBorder.none,
      labelText: 'Name',
    ),
    // other arguments
  )

Using InputDecoration.collapsed

The InputDecoration.collapsed is a named constructor that creates a new InputDecoration whose size is the same as the input field and doesn't include the border by default . If you look at the constructor, the border is set to InputBorder.none (but not with the other state-specific arguments). Using this way, you'll notice a size different since it's adjusted to be the same as the input field. In addition, it may have a border if there is any state-specific border set in the theme.

  TextField(
    decoration: const InputDecoration.collapsed(
      hintText: 'Name'
    ),
    // other arguments
  )

Output:

Flutter - Text Field - InputDecoration.collapsed

Remove Border/Underline Using ThemeData

If you have a lot of text fields and you want them to be rendered without any underline or border, you can set the default style in the theme. If you use MateialApp, there is an argument named theme where you can set a custom ThemeData. In the ThemeData, you have to define an InputDecorationTheme which doesn't have any border. The arguments that you need to pass to the InputDecorationTheme for customizing the border are the same as those passed to the InputDecoration.

Setting only the border argument to InputBorder.none should be enough to remove the border, unless there is any state-specific argument that overrides the shape.

You also need to know that the theme's style can be overridden if the field has its own InputDecoration that defines the same argument. So, it's still possible to add a border on specific fields.

  MaterialApp(
    theme: ThemeData.light().copyWith(
      inputDecorationTheme: const InputDecorationTheme(
        border: InputBorder.none,
      )
    )
  )

Full Code

Below is the full code that renders a text field without a border or underline. There are also switches for changing the state of the input field, so you can see how it looks in each state.

  import 'package:flutter/material.dart';
  
  void main() => runApp(const MyApp());
  
  class MyApp extends StatelessWidget {
  
    const MyApp({Key? key}) : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Woolha.com Flutter Tutorial',
        theme: ThemeData.light().copyWith(
          inputDecorationTheme: const InputDecorationTheme(
            // border: InputBorder.none,
          )
        ),
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Woolha.com Flutter Tutorial'),
            backgroundColor: Colors.teal,
          ),
          body: const Home(),
        ),
      );
    }
  }
  
  class Home extends StatefulWidget {
    const Home({super.key});
  
    @override
    State<StatefulWidget> createState() {
      return HomeState();
    }
  }
  class HomeState extends State<Home> {
  
    bool _isError = false;
    bool _isFocused = false;
    bool _isDisabled = false;
    final FocusNode _focusNode = FocusNode();
  
    @override
    Widget build(BuildContext context) {
      return Padding(
        padding: const EdgeInsets.all(10),
        child: Column(
          children: [
            TextField(
              focusNode: _focusNode,
              decoration: const InputDecoration(
                border: InputBorder.none,
                // The commented arguments only necessary if you define it in the theme.
                // enabledBorder: InputBorder.none,
                // disabledBorder: InputBorder.none,
                // focusedBorder: InputBorder.none,
                // errorBorder: InputBorder.none,
                // focusedErrorBorder: InputBorder.none,
                labelText: 'Name',
              ),
              onTap: () => setState(() => _isFocused = true),
              enabled: !_isDisabled,
            ),
            const SizedBox(height: 50),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Column(
                  children: [
                    const Text('Is Disabled'),
                    Switch(
                      value: _isDisabled,
                      onChanged: (val) => setState(() {
                        _isDisabled = val;
                      }),
                    ),
                  ],
                ),
                Column(
                  children: [
                    const Text('Is Error'),
                    Switch(
                      value: _isError,
                      onChanged: (val) => setState(() {
                        _isError = val;
                      }),
                    ),
                  ],
                ),
                Column(
                  children: [
                    const Text('Is Focused'),
                    Switch(
                      value: _isFocused,
                      onChanged: (isFocused) => setState(() {
                        if (isFocused) {
                          _focusNode.requestFocus();
                        } else {
                          _focusNode.unfocus();
                        }
  
                        setState(() {
                          _isFocused = isFocused;
                        });
                      }),
                    ),
                  ],
                ),
              ],
            )
          ],
        ),
      );
    }
  }

Summary

To remove the border or underline of a TextField or TextInputField, you need to create an InputDecoration where the border is set to InputBorder.none. The InputDecoration has some arguments for setting the border based on the state of the field. Setting only the border argument should be enough if there is no state-specific argument set. There is also a named constructor InputDecoration.collapsed that creates an InputDecorator whose size is the same as the input field and sets the border argument to InputBorder.none by default. It's also possible to set it in the ThemeData which affects all the text fields in the application.

You can also read about: