Handle changes to a text field
In some cases, it’s useful to run a callback function every time the text in a text field changes. For example, you might want to build a search screen with autocomplete functionality where you want to update the results as the user types.
How do you run a callback function every time the text changes? With Flutter, you have two options:
- Supply an
onChanged()callback to aTextFieldor aTextFormField. - Use a
TextEditingController.
1. Supply an onChanged() callback to a TextField or a TextFormField
The simplest approach is to supply an onChanged() callback to a
TextField or a TextFormField.
Whenever the text changes, the callback is invoked.
In this example, print the current value of the text field to the console every time the text changes.
TextField(
onChanged: (text) {
print('First text field: $text');
},
),
2. Use a TextEditingController
A more powerful, but more elaborate approach, is to supply a
TextEditingController as the controller
property of the TextField or a TextFormField.
To be notified when the text changes, listen to the controller
using the addListener() method using the following steps:
- Create a
TextEditingController. - Connect the
TextEditingControllerto a text field. - Create a function to print the latest value.
- Listen to the controller for changes.
Create a TextEditingController
Create a TextEditingController:
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
@override
_MyCustomFormState createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller. Later, use it to retrieve the
// current value of the TextField.
final myController = TextEditingController();
@override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
myController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Fill this out in the next step.
}
}
Connect the TextEditingController to a text field
Supply the TextEditingController to either a TextField
or a TextFormField. Once you wire these two classes together,
you can begin listening for changes to the text field.
TextField( controller: myController, ),
Create a function to print the latest value
You need a function to run every time the text changes.
Create a method in the _MyCustomFormState class that prints
out the current value of the text field.
void _printLatestValue() {
print('Second text field: ${myController.text}');
}Listen to the controller for changes
Finally, listen to the TextEditingController and call the
_printLatestValue() method when the text changes. Use the
addListener() method for this purpose.
Begin listening for changes when the
_MyCustomFormState class is initialized,
and stop listening when the _MyCustomFormState is disposed.
@override
void initState() {
super.initState();
// Start listening to changes.
myController.addListener(_printLatestValue);
}@override
void dispose() {
// Clean up the controller when the widget is removed from the widget tree.
// This also removes the _printLatestValue listener.
myController.dispose();
super.dispose();
}Interactive example
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Retrieve Text Input',
home: MyCustomForm(),
);
}
}
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
@override
_MyCustomFormState createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller and use it to retrieve the current value
// of the TextField.
final myController = TextEditingController();
@override
void initState() {
super.initState();
// Start listening to changes.
myController.addListener(_printLatestValue);
}
@override
void dispose() {
// Clean up the controller when the widget is removed from the widget tree.
// This also removes the _printLatestValue listener.
myController.dispose();
super.dispose();
}
void _printLatestValue() {
print('Second text field: ${myController.text}');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Retrieve Text Input'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
onChanged: (text) {
print('First text field: $text');
},
),
TextField(
controller: myController,
),
],
),
),
);
}
}