Dart Tutorial

Flutter Lesson 28 of 83 3 min read

TextField and TextEditingController in Flutter

Learn TextField in Flutter: read input with onChanged and controllers, decorate fields, set keyboard types, handle focus and passwords.

On this page

TextField lets the user type. There are two ways to get what they typed.

onChanged: react to every keystroke #

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: GreetingPage()));

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

  @override
  State<GreetingPage> createState() => _GreetingPageState();
}

class _GreetingPageState extends State<GreetingPage> {
  String _name = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              decoration: const InputDecoration(
                labelText: 'Your name',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) => setState(() => _name = value),
            ),
            const SizedBox(height: 24),
            Text(_name.isEmpty ? 'Hello!' : 'Hello, $_name!'),
          ],
        ),
      ),
    );
  }
}

TextEditingController: read or change the text any time #

A controller holds the field’s text. Use it when you need the value later, such as on a button press, or need to set or clear the text from code.

class _SearchBoxState extends State<SearchBox> {
  final _controller = TextEditingController();

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

  void _search() {
    debugPrint('Searching for ${_controller.text}');
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      decoration: InputDecoration(
        hintText: 'Search products',
        prefixIcon: const Icon(Icons.search),
        suffixIcon: IconButton(
          icon: const Icon(Icons.clear),
          onPressed: _controller.clear,
        ),
      ),
      textInputAction: TextInputAction.search,
      onSubmitted: (_) => _search(),
    );
  }
}

Create the controller in the State class, never inside build, and dispose it.

InputDecoration #

PropertyShows
labelTextA label that floats above the field when focused
hintTextGrey placeholder text
helperTextA note below the field
errorTextA red message. Also turns the border red
prefixIcon, suffixIconIcons inside the field
prefixText, suffixTextFixed text, such as Rs. or kg
borderOutlineInputBorder() or the default underline
filled, fillColorA background
counterTextCharacter count

Set the look once for the whole app with inputDecorationTheme in your theme.

The right keyboard #

const TextField(keyboardType: TextInputType.emailAddress)
const TextField(keyboardType: TextInputType.phone)
const TextField(keyboardType: TextInputType.number)
const TextField(keyboardType: TextInputType.multiline, maxLines: 4)

keyboardType changes the keys shown. It does not restrict what can be entered. To do that, add input formatters:

import 'package:flutter/services.dart';

TextField(
  keyboardType: TextInputType.number,
  inputFormatters: [
    FilteringTextInputFormatter.digitsOnly,
    LengthLimitingTextInputFormatter(10),
  ],
)

Passwords #

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

Other useful properties #

PropertyEffect
maxLengthLimit and show a counter
maxLinesLines visible. null grows without limit
textCapitalizationwords for names, sentences for messages
textInputActionThe action key: next, done, search, send
autofocusFocus as soon as the screen opens
enabled, readOnlyDisable or lock
autofillHintsLets the system offer saved emails and passwords
onSubmittedCalled when the action key is pressed

Focus #

A FocusNode lets you move the cursor between fields or dismiss the keyboard.

final _passwordFocus = FocusNode();

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

// Hide the keyboard:
FocusScope.of(context).unfocus();

// Dispose the node in dispose().

With textInputAction: TextInputAction.next, Flutter already moves to the next field without a focus node in most layouts.

Do not put a controller’s text in setState on every key #

If you use a controller and only need the value on submit, you do not need onChanged or setState at all. To rebuild part of the UI as the text changes, listen to the controller:

ValueListenableBuilder<TextEditingValue>(
  valueListenable: _controller,
  builder: (context, value, child) => Text('${value.text.length} characters'),
)

Try it yourself #

Build a “new message” box: a multi-line field with a 140-character limit, a live count of remaining characters, and a Send button that is disabled while the field is empty and clears the field when pressed.

Practise in the playground Updated by Santosh Adhikari