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 aTextField
or aTextFormField
. - Use a
TextEditingController
.
onChanged()
callback to a TextField
or a TextFormField
1. Supply an 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');
},
),
TextEditingController
2. Use a 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
TextEditingController
to a text field. - Create a function to print the latest value.
- Listen to the controller for changes.
TextEditingController
Create a Create a TextEditingController
:
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
@override
State<MyCustomForm> 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.
}
}
TextEditingController
to a text field
Connect the 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({super.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({super.key});
@override
State<MyCustomForm> 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,
),
],
),
),
);
}
}