Flutter - Using TextEditingController Examples

This tutorial shows you how to use TextEditingController on TextField or TextFormField in Flutter, including how to set the initial value, getting and setting text and selection values, as well as building TextSpan from it.

If you have an editable text widget, you need to store the value somewhere. The simplest approach is by using a state variable to store the value of text. There is another powerful way by setting a TextEditingController as the controller of a TextField or TextFormField.

Whenever the user modifies the text field, the controller notifies its listeners so that they know the text and selection properties. TextEditingController also allows you to modify the text and selection. In this case, the controller will notify the text field so that it can update the view.

Creating TextEditingController

To create a new TextEditingController, just call the constructor.

  TextEditingController({ String text })

You can call it without the parameter, which means the initial text is empty. Usually it's stored as a state variable.

  final _myController = TextEditingController()

Optionally you can also set the initial value by passing text parameter.

  final _myController = TextEditingController(text: 'Woolha')

Another way to create a TextEditingController is by using fromValue named constructor which accepts a TextEditingValue as a required parameter.

  TextEditingController.fromValue(TextEditingValue value)

Below is the example of using fromValue.

  final _myController = TextEditingController.fromValue(TextEditingValue(
    text: 'Woolha',
  ));

When the widget is disposed, you also need to clean up the controller.

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

 

Binding to TextField or TextFormField

The controller needs to be bound to a text field in order to listen for value changes on the field.

  TextFormField(
    key: Key('name'),
    controller: _myController,
    decoration: InputDecoration(
        labelText: 'Name'
    ),
  ),

 

Getting text and selection Values

To get the value, just access the text property of the controller.

  _myController.text

If you need to get information about TextSelection (which includes offset, affinity, etc.), use the selection property.

  _myController.selection

 

Setting text and selection Values

You can set the text value programmatically by assigning the text property with a new value. By default the cursor will move to the beginning of the text. If you want to set the cursor elsewhere, you have to set the selection property, as shown below.

  String newText = 'Woolha.com';
  _myController.text = newText;
  _myController.selection = TextSelection.collapsed(offset: newText.length);

For clearing the text, Flutter provides clear() method.

  _myController.clear();

 

Building TextSpan

You can easily build a TextSpan from a TextEditingController.

  TextSpan buildTextSpan({TextStyle style , bool withComposing})

style is the TextStyle to be applied on the generated TextSpan. withComposing is used to make the text appears in underline if value.composing.isValid is true.

Here's the example.

  _myController.buildTextSpan(
    style: TextStyle(color: Colors.red),,
    withComposing: false
  );

Although both parameters are optional, I recommend you to set withComposing as sometimes it may have null value.

 

Below is a simple app that utilizes TextEditingController.

  import 'package:flutter/material.dart';
  import 'package:flutter/services.dart';
  
  void main() => runApp(MyApp());
  
  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Welcome to Flutter',
        home: TextEditingControllerExample(),
      );
    }
  }
  
  class TextEditingControllerExample extends StatefulWidget {
    @override
    _TextEditingControllerExampleState createState() {
      return _TextEditingControllerExampleState();
    }
  }
  
  class _TextEditingControllerExampleState extends State<TextEditingControllerExample> {
  
    final _myController = TextEditingController(text: 'Woolha');
    TextSpan _myTextSpan = TextSpan();
  
    @override
    void dispose() {
      _myController.dispose();
      super.dispose();
    }
  
  
    @override
    Widget build(BuildContext context) {
      print("build");
  
      return Scaffold(
        appBar: AppBar(
          title: Text('Woolha.com - Flutter Tutorial'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: <Widget>[
              Form(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    TextFormField(
                      key: Key('name'),
                      controller: _myController,
                      decoration: InputDecoration(
                          labelText: 'Name'
                      ),
                    ),
                    Wrap(
                      spacing: 20,
                      children: <Widget>[
                        RaisedButton(
                          onPressed: () {
                            return showDialog(
                              context: context,
                              builder: (context) {
                                return AlertDialog(
                                  content: Text('${_myController.text}\n${_myController.selection.toString()}'),
                                );
                              },
                            );
                          },
                          child: Text('Show'),
                        ),
                        RaisedButton(
                          onPressed: () {
                            String newText = 'Woolha.com';
                            _myController.text = newText;
                          _myController.selection = TextSelection.collapsed(offset: newText.length);
                          },
                          child: Text('Set Text'),
                        ),
                        RaisedButton(
                          onPressed: () {
                            _myController.clear();
                          },
                          child: Text('Clear'),
                        ),
                        RaisedButton(
                          onPressed: () {
                            setState(() {
                              _myTextSpan = _myController.buildTextSpan(
                                style: TextStyle(color: Colors.red),
                                withComposing: false
                              );
                            });
                          },
                          child: Text('Build TextSpan'),
                        ),
                      ],
                    )
                  ],
                ),
              ),
              RichText(
                text: _myTextSpan,
              )
            ],
          )
        ),
      );
    }
  }