Flutter TextField and TextEditingController: Input, Focus, and Listeners

Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter TextField and TextEditingController guide, with an input field, a controller reading text, a FocusNode, and InputDecoration labels.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter TextField and TextEditingController guide.

Text input is where most forms begin, and Flutter's TextField is deceptively simple — the depth is in the TextEditingController that reads and writes its value, the FocusNode that moves the cursor, and the InputDecoration that gives it labels and icons. This guide covers reading and setting text, listening for changes, managing focus, decorating the field, keyboard types and obscured passwords, and the one cleanup step everyone forgets, with paste-ready code.

The Complete Flutter Guide course thumbnail

The Complete Flutter Guide: Build Android, iOS and Web apps

Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.

Enrol now

Every snippet below is paste-ready against current stable Flutter. TextField is the foundation of every form; pair it with form validation once you wrap fields in a Form.

Follow me on Instagram@sagnikteaches

We'll read text with a controller, listen for changes, decorate the field, manage focus, set keyboard types, and dispose the controller cleanly.

Connect on LinkedInSagnik Bhattacharya

If you'd rather watch a login and search form wired up live, the channel builds real input flows step by step.

Subscribe on YouTube@codingliquids

Read text with a controller

To read what the user typed, attach a TextEditingController and read its .text when you need the value — usually in a button. Create it as a field and dispose it.

final _controller = TextEditingController();

// In build:
TextField(controller: _controller)

// Read the value on submit:
ElevatedButton(
  onPressed: () => print(_controller.text),
  child: const Text('Submit'),
)

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

The controller is the single source of truth for the field's text. You can also set it — _controller.text = 'preset' — or empty it with _controller.clear(), which is handy for a clear-search button.

onChanged vs onSubmitted

For reacting as the user types, use the callbacks rather than the controller. onChanged fires on every keystroke; onSubmitted fires once when the user presses the keyboard action key.

TextField(
  onChanged: (value) => setState(() => _query = value), // live search
  onSubmitted: (value) => _runSearch(value),            // pressed Done
)

Use onChanged for search-as-you-type and live validation, and onSubmitted for the single "I'm finished" moment. For continuous listening with side effects, you can also add a listener to the controller — but remember to remove it when disposing.

Decorate with InputDecoration

A bare TextField is just an underline. InputDecoration adds the label, hint, helper and error text, icons, and borders that make a field readable.

TextField(
  decoration: const InputDecoration(
    labelText: 'Email',
    hintText: 'you@example.com',
    prefixIcon: Icon(Icons.email_outlined),
    border: OutlineInputBorder(),
  ),
)

The labelText floats above the field when focused, the hintText shows inside when empty, and OutlineInputBorder gives the boxed look most designs expect. See the icons guide for choosing the prefix and suffix icons.

The Complete Flutter Guide course thumbnail

Build forms users trust

The Complete Flutter Guide covers inputs, validation, focus, and full forms inside real apps.

Enrol now

Manage focus

A FocusNode lets you move the cursor between fields and dismiss the keyboard. Pass it to the field, then call requestFocus to focus or move to the next field on submit.

final _emailFocus = FocusNode();
final _passwordFocus = FocusNode();

TextField(
  focusNode: _emailFocus,
  textInputAction: TextInputAction.next,
  onSubmitted: (_) =>
      FocusScope.of(context).requestFocus(_passwordFocus),
)

Dispose every FocusNode alongside its controller. To dismiss the keyboard when the user taps elsewhere, call FocusScope.of(context).unfocus() — a small touch that makes forms feel finished.

Keyboard types and passwords

Set keyboardType so the right on-screen keyboard appears — TextInputType.emailAddress adds the @ key, TextInputType.number shows a numeric pad. For passwords, obscureText: true hides each character behind a dot.

TextField(
  obscureText: _hidePassword,
  keyboardType: TextInputType.visiblePassword,
  decoration: InputDecoration(
    labelText: 'Password',
    suffixIcon: IconButton(
      icon: Icon(_hidePassword ? Icons.visibility : Icons.visibility_off),
      onPressed: () => setState(() => _hidePassword = !_hidePassword),
    ),
  ),
)

Toggling obscureText from a suffix icon gives the show/hide password button users expect. Matching the keyboard to the field is a small detail that noticeably improves the typing experience.

Common mistakes

  • Not disposing the controller or FocusNode. Both leak if you skip dispose.
  • Reading controller.text in build. Read it on demand, not on every rebuild.
  • Using onChanged when onSubmitted is meant. Per-keystroke work can be wasteful; pick the right callback.
  • Forgetting keyboardType. Email and number fields should show the matching keyboard.
  • No way to dismiss the keyboard. Add unfocus on tap-outside so the keyboard can close.

Frequently asked questions

How do I read the text from a TextField in Flutter?

Attach a TextEditingController and read controller.text when needed; create it as a field and dispose it in dispose.

What is the difference between onChanged and onSubmitted in a Flutter TextField?

onChanged fires on every keystroke for live reactions; onSubmitted fires once when the user presses the keyboard action key.

How do I manage focus in a Flutter TextField?

Use a FocusNode with requestFocus to move between fields and FocusScope.of(context).unfocus() to dismiss the keyboard; dispose every node.

How do I hide password text in a Flutter TextField?

Set obscureText: true, and toggle it from an InputDecoration suffix icon to add a show/hide button.

Further reads

Keep going with the tutorials that pair with this guide:

Sources: Flutter documentation — TextField, TextEditingController, FocusNode, InputDecoration, TextInputType, and the "Create and style a text field" and "Retrieve the value of a text field" cookbook recipes (docs.flutter.dev). Verified against current stable Flutter.