In some cases, it can be handy to run a callback function every time the textin a text field changes. For example, we might want to build a search screenwith autocomplete functionality. In this case, we would want to update theresults as the user types.

How can we run a callback function every time the text changes? With Flutter,we have two options:

  • Supply an onChanged callback to a TextField
  • Use a TextEditingController

1. Supply an onChanged callback to a TextField

The simplest approach is to supply anonChangedcallback to aTextField.Whenever the text changes, the callback will be invoked. One downside to thisapproach is it does not work with TextFormField Widgets.

In this example, we will print the current value of the text field to theconsole every time the text changes.

  1. TextField(
  2. onChanged: (text) {
  3. print("First text field: $text");
  4. },
  5. );

2. Use a TextEditingController

A more powerful, but more elaborate approach, is to supply aTextEditingControlleras thecontrollerproperty of the TextField or a TextFormField.

To be notified when the text changes, we can listen to the controller using itsaddListenermethod.

Directions

  • Create a TextEditingController
  • Supply the TextEditingController to a TextField
  • Create a function to print the latest value
  • Listen to the controller for changes

Create a TextEditingController

First, we’ll need to create a TextEditingController. In the subsequent steps,we will supply the TextEditingController to a TextField. Once we’ve wiredthese two classes together, we can listen for changes to the text field!

  1. // Define a Custom Form Widget
  2. class MyCustomForm extends StatefulWidget {
  3. @override
  4. _MyCustomFormState createState() => _MyCustomFormState();
  5. }
  6. // Define a corresponding State class. This class will hold the data related to
  7. // our Form.
  8. class _MyCustomFormState extends State<MyCustomForm> {
  9. // Create a text controller. We will use it to retrieve the current value
  10. // of the TextField!
  11. final myController = TextEditingController();
  12. @override
  13. void dispose() {
  14. // Clean up the controller when the Widget is removed from the Widget tree
  15. myController.dispose();
  16. super.dispose();
  17. }
  18. @override
  19. Widget build(BuildContext context) {
  20. // We will fill this out in the next step!
  21. }
  22. }

Note: Please remember to dispose the TextEditingController when it is nolonger needed. This will ensure we discard any resources used by the object.

Supply the TextEditingController to a TextField

In order to work, the TextEditingController must be supplied to either aTextField or a TextFormField. Once it’s wired up, we can begin listeningfor changes to the text field.

  1. TextField(
  2. controller: myController,
  3. );

Create a function to print the latest value

Now, we’ll need a function that should run every time the text changes! In thisexample, we’ll create a method that prints out the current value of the textfield.

This method will live inside our _MyCustomFormState class.

  1. _printLatestValue() {
  2. print("Second text field: ${myController.text}");
  3. }

Listen to the controller for changes

Finally, we need to listen to the TextEditingController and run the_printLatestValue method whenever the text changes. We will use theaddListenermethod to achieve this task.

In this example, we will begin listening for changes when the_MyCustomFormState class is initialized, and stop listening when the_MyCustomFormState is disposed.

  1. class _MyCustomFormState extends State<MyCustomForm> {
  2. @override
  3. void initState() {
  4. super.initState();
  5. // Start listening to changes
  6. myController.addListener(_printLatestValue);
  7. }
  8. }

Complete example

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget {
  4. @override
  5. Widget build(BuildContext context) {
  6. return MaterialApp(
  7. title: 'Retrieve Text Input',
  8. home: MyCustomForm(),
  9. );
  10. }
  11. }
  12. // Define a Custom Form Widget
  13. class MyCustomForm extends StatefulWidget {
  14. @override
  15. _MyCustomFormState createState() => _MyCustomFormState();
  16. }
  17. // Define a corresponding State class. This class will hold the data related to
  18. // our Form.
  19. class _MyCustomFormState extends State<MyCustomForm> {
  20. // Create a text controller. We will use it to retrieve the current value
  21. // of the TextField!
  22. final myController = TextEditingController();
  23. @override
  24. void initState() {
  25. super.initState();
  26. myController.addListener(_printLatestValue);
  27. }
  28. @override
  29. void dispose() {
  30. // Clean up the controller when the Widget is removed from the Widget tree
  31. // This also removes the _printLatestValue listener
  32. myController.dispose();
  33. super.dispose();
  34. }
  35. _printLatestValue() {
  36. print("Second text field: ${myController.text}");
  37. }
  38. @override
  39. Widget build(BuildContext context) {
  40. return Scaffold(
  41. appBar: AppBar(
  42. title: Text('Retrieve Text Input'),
  43. ),
  44. body: Padding(
  45. padding: const EdgeInsets.all(16.0),
  46. child: Column(
  47. children: <Widget>[
  48. TextField(
  49. onChanged: (text) {
  50. print("First text field: $text");
  51. },
  52. ),
  53. TextField(
  54. controller: myController,
  55. ),
  56. ],
  57. ),
  58. ),
  59. );
  60. }
  61. }