TextEditingController vs OnChanged
You are just not setting state synchronously that's all. What onChanged
does is exactly possible with this approach:
class _TestFormState extends State<TestForm> {
late TextEditingController controller;
@override
void initState() {
controller = TextEditingController()
..addListener(() {
setState(() {});
});
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text('Current Value: ${controller.text}'),
TextField(
controller: controller,
),
],
);
}
}
As you see, we have listener that setting state every time state of the controller changes. This is exactly what onChanged
does.
So, about benefits, you can achieve everything with both approach, it's a subjective way.
About benefits:
If you need to hold field values within Stream
, onChanged
is what you need. In other cases you may use controller
.
Actually you won't need both in most of time in my opinion because TextFormField + Form
within StatefulWidget
is quite complete way to implement form pages. Checkout cookbook: https://flutter.dev/docs/cookbook/forms/validation
TextEditingController actually is managing his own state, that's why you can see the input on the screen once you change it.
You have 2 problems here, the first is that you are not adding any listener to the TextEditingController, you are just asking "give me the current value" only when you build the widget, not "give me the value any time it changes". To achieve this you need to add a listener to the text controller and it will be called every time that the value change.
Try this :
@override
void initState() {
super.initState();
// Start listening to changes.
ctrl.addListener(_printValue);
}
_printValue() {
print("Value: ${ctrl.text}");
}
This will work because print doesn't need to render anything on the screen but if you change it to return a widget it will not work either. That is the second problem, as you pointed out, your parent widget is not been rebuild when the value change, in this case you cannot avoid the setState (or other way to tell flutter that needs to rebuild the widget) when the value change because you need to rebuild the widget to view the change.
Another thing that ill like to point out is that TextEditingController is much powerful and it can be used for more things that just add notifiers to changes. For example if you want a button on other part of the screen that clear the text on a TextField you will need a TextEditingController binded to that field.
Hope it helps!