flutter
/

Flutter TextField – Complete Guide with Examples

Last Sync: Today

On this page

13
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Flutter TextField – Complete Guide with Examples

What is TextField in Flutter?

TextField is a Material Design widget that allows users to enter text. It's one of the most commonly used input widgets in Flutter. For forms with validation, you'll often use TextFormField, which is a wrapper around TextField that integrates with the Form widget. This guide covers both, with a focus on practical usage, customization, and best practices.

Basic TextField

DARTRead-only
1
TextField(
  decoration: InputDecoration(
    labelText: 'Enter your name',
    hintText: 'e.g., John Doe',
    border: OutlineInputBorder(),
  ),
)

The simplest form of a TextField uses an InputDecoration to provide labels, hint text, borders, and icons. The labelText appears inside the field when empty and moves above when focused.

Controlling Text with TextEditingController

To read or modify the text programmatically, use a TextEditingController. You can also listen to changes with addListener.

DARTRead-only
1
final _controller = TextEditingController();

@override
void dispose() {
  _controller.dispose();
  super.dispose();
}

TextField(
  controller: _controller,
  decoration: InputDecoration(labelText: 'Name'),
)

// Read the current text
String text = _controller.text;

// Set text
_controller.text = 'Initial value';

// Listen to changes
_controller.addListener(() {
  print(_controller.text);
});

Input Decoration

InputDecoration is used to customize the appearance. Key properties:

    • labelText: A label that animates above the field when focused.
    • hintText: A hint that disappears when the user starts typing.
    • prefixIcon / suffixIcon: Icons at the start/end of the field.
    • prefixText / suffixText: Text before/after the input.
    • border / enabledBorder / focusedBorder: Define the field's border.
    • filled and fillColor: For filled background.
    • errorText: Displays an error message below the field.
    • counterText: Character counter text.
DARTRead-only
1
TextField(
  decoration: InputDecoration(
    labelText: 'Username',
    hintText: 'Choose a unique username',
    prefixIcon: Icon(Icons.person),
    suffixIcon: Icon(Icons.check),
    filled: true,
    fillColor: Colors.grey.shade100,
    border: OutlineInputBorder(
      borderRadius: BorderRadius.circular(12),
    ),
    errorText: _hasError ? 'Username already taken' : null,
    counterText: '${_controller.text.length}/20',
  ),
)

Keyboard Types and Actions

Control the keyboard type and action buttons with keyboardType and textInputAction.

DARTRead-only
1
TextField(
  keyboardType: TextInputType.emailAddress,
  textInputAction: TextInputAction.next,
  decoration: InputDecoration(labelText: 'Email'),
)

TextField(
  keyboardType: TextInputType.number,
  textInputAction: TextInputAction.done,
  decoration: InputDecoration(labelText: 'Age'),
)

Common TextInputAction values: next, done, go, search, send. The action button on the keyboard changes accordingly.

Obscure Text (Password Fields)

DARTRead-only
1
TextField(
  obscureText: true,
  decoration: InputDecoration(labelText: 'Password'),
)

Focus Management

Use FocusNode to control focus programmatically and respond to focus changes. Always dispose of focus nodes to avoid memory leaks.

DARTRead-only
1
final _focusNode = FocusNode();

@override
void dispose() {
  _focusNode.dispose();
  super.dispose();
}

TextField(
  focusNode: _focusNode,
  decoration: InputDecoration(labelText: 'Focus me'),
)

// Request focus
_focusNode.requestFocus();

// Remove focus
_focusNode.unfocus();

// Listen to focus changes
_focusNode.addListener(() {
  if (_focusNode.hasFocus) {
    print('Field focused');
  }
});

For moving focus between fields, use FocusScope.of(context).nextFocus() or set textInputAction to TextInputAction.next and handle onFieldSubmitted.

Input Formatters

inputFormatters allow you to restrict or modify the input as the user types. Common formatters: FilteringTextInputFormatter, LengthLimitingTextInputFormatter, and custom ones.

DARTRead-only
1
import 'package:flutter/services.dart';

TextField(
  inputFormatters: [
    FilteringTextInputFormatter.digitsOnly, // only digits
    LengthLimitingTextInputFormatter(10),  // max 10 characters
  ],
  decoration: InputDecoration(labelText: 'Phone'),
)

// Custom formatter example
class CapitalizeFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    return newValue.copyWith(
      text: newValue.text.toUpperCase(),
    );
  }
}

TextFormField for Forms

TextFormField is a variant that integrates with Form and provides validation and saving capabilities. Use it inside a Form widget with a GlobalKey<FormState>.

DARTRead-only
1
final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(
        decoration: InputDecoration(labelText: 'Name'),
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'Please enter your name';
          }
          return null;
        },
        onSaved: (value) {
          // Save the value to a variable
        },
      ),
      ElevatedButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            _formKey.currentState!.save();
            // Process data
          }
        },
        child: Text('Submit'),
      ),
    ],
  ),
)

Handling User Input in Real Time

DARTRead-only
1
TextField(
  onChanged: (value) {
    // Called every time the text changes
    setState(() {
      _text = value;
    });
  },
  onSubmitted: (value) {
    // Called when the user presses the submit button on the keyboard
    print('Submitted: $value');
  },
)

Best Practices

    • Always dispose of TextEditingController and FocusNode objects to prevent memory leaks.
    • Use TextFormField with Form for validation and saving.
    • Provide clear InputDecoration with appropriate labelText and hintText.
    • For numeric inputs, set keyboardType: TextInputType.number and consider using inputFormatters to enforce digits only.
    • Use textInputAction to control keyboard behavior (next, done, etc.).
    • Avoid heavy logic inside onChanged; use throttling or debouncing if needed.

Common Mistakes

    • Not disposing controllers and focus nodes – leads to memory leaks.
    • Using TextField inside a Form without TextFormField – validation won't work.
    • Forgetting to wrap TextField in a Material or MaterialApp – text field will look plain and won't get Material styling.
    • Using onChanged for validation without debouncing – may cause performance issues.
    • Not handling the case when controller.text is null – always check before using.
    • Setting obscureText to true but still storing password in plain text – remember to hash before storing.

Key Takeaways

    • Use TextField for simple text input; TextFormField for validation and forms.
  • -Control text with TextEditingController.
    • Customize appearance with InputDecoration.
    • Manage focus with FocusNode.
    • Restrict input with inputFormatters.
    • Use keyboardType and textInputAction for keyboard behavior.
    • Always dispose of resources in dispose().

Try it yourself

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TextField Demo',
      home: Scaffold(
        appBar: AppBar(title: const Text('TextField Example')),
        body: Padding(
          padding: const EdgeInsets.all(20),
          child: const TextFieldDemo(),
        ),
      ),
    );
  }
}

class TextFieldDemo extends StatefulWidget {
  const TextFieldDemo({super.key});

  @override
  _TextFieldDemoState createState() => _TextFieldDemoState();
}

class _TextFieldDemoState extends State<TextFieldDemo> {
  final _controller = TextEditingController();
  final _focusNode = FocusNode();
  String _lastSubmitted = '';

  @override
  void dispose() {
    _controller.dispose();
    _focusNode.dispose();
    super.dispose();
  }

  void _submit() {
    setState(() {
      _lastSubmitted = _controller.text;
    });
    _controller.clear();
    _focusNode.unfocus();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        TextField(
          controller: _controller,
          focusNode: _focusNode,
          decoration: InputDecoration(
            labelText: 'Enter something',
            hintText: 'Type here...',
            border: OutlineInputBorder(),
            suffixIcon: _controller.text.isNotEmpty
                ? IconButton(
                    icon: const Icon(Icons.clear),
                    onPressed: () => _controller.clear(),
                  )
                : null,
          ),
          onSubmitted: (_) => _submit(),
        ),
        const SizedBox(height: 20),
        ElevatedButton(
          onPressed: _submit,
          child: const Text('Submit'),
        ),
        const SizedBox(height: 20),
        Text('Last submitted: $_lastSubmitted'),
      ],
    );
  }
}

Test Your Knowledge

Q1
of 4

Which widget is used to read and modify the text programmatically?

A
TextEditingController
B
TextInputFormatter
C
FocusNode
D
InputDecoration
Q2
of 4

How do you set a TextField to only accept numbers?

A
Set `keyboardType: TextInputType.number`
B
Use `inputFormatters: [FilteringTextInputFormatter.digitsOnly]`
C
Both A and B
D
Use `obscureText: true`
Q3
of 4

What method must be called to dispose of a TextEditingController?

A
dispose()
B
clear()
C
release()
D
close()
Q4
of 4

Which property is used to show a message below the TextField when validation fails?

A
errorText
B
helperText
C
labelText
D
hintText

Frequently Asked Questions

What is the difference between `TextField` and `TextFormField`?

TextField is a basic text input widget. TextFormField is a convenience widget that wraps TextField and adds integration with Form – it includes validator and onSaved callbacks, and can be automatically validated by the parent Form.

How do I clear a TextField programmatically?

Call _controller.clear() on the associated TextEditingController. This also works with TextFormField.

How do I set the initial value of a TextField?

Either set _controller.text = 'initial' before building, or use the initialValue property of TextFormField (but note that initialValue is overridden by the controller's text if both are set).

How do I create a search field with a clear button?

Add a suffixIcon that calls _controller.clear() when tapped. Also listen to changes to show the clear button only when text is not empty.

Can I use custom fonts in TextField?

Yes, use the style property with a TextStyle that includes fontFamily.

Previous

flutter form

Next

flutter button

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.