I tried to create a simple form to validate user input data.
class UserForm extends StatelessWidget {
final _formKey = GlobalKey<FormState>();
final TextEditingController usernameController = TextEditingController();
UserForm({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
TextFormField(
controller: usernameController,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter something';
}
return null;
},
autovalidateMode: AutovalidateMode.onUserInteraction,
),
ElevatedButton(
onPressed: () {
print(usernameController.text);
},
child: const Text('Check'),
),
),
);
}
}
When I type something and delete it after, I get the error message "Please enter something under the textfield".
I saw an official demo example with a form validation here: https://flutter.dev/docs/cookbook/forms/validation.
Now, my question is: why do we need a StatefulWidget for a form, if the error message gets displayed in a StatelessWidget. The value of TextFormField is changing, not the custom form widget I created.
Can somebody explain me when to use stateless and when stateful?
CodePudding user response:
Actually you can use stateless or stateful widget for hosting your form widgets. The state will be preserved in those fields themselves.
But there's a catch. If you need to access those fields' content or control them you'll need to use TextEditingController
. Of course you can instantiate TextEditingController
inside your stateless widget but when your widget tree updates the text controller will be re-instantiated, so all data will be lost inside it (cursor position and text value).
So, to preserve controller state you have to create it inside stateful widget or use some sort of state management or dependency injection to store it somewhere that'll survive the next update.
Also TextEditingController
could lead to memory leaks in your app, so it's best advice to destroy them after you've used it. You can do this by overriding dispose
method of State
class.
class MyWidgetState extends State<MyWidget> {
final controller = TextEditingController();
@override
void dispose() {
super.dispose();
controller.dispose();
}
@override
Widget build(BuildContext context) {
// ...
}
}
Conclusion
Use State
class whenever you need to persist a value / variable. The widget class will be re-created after each build so all the values inside it will be lost (or will cause memory leak if not cleaned in proper way) and replaced with a new instance. The update doesn't happens regularly so the chance of happening this low but if the parent widget that's hosting your form widget changes its state then the updates will affect the form widget will be rebuilt as well.
In general widgets are just scaffold for your widget tree. They should just hold immutable data and when you need to change it you have to use mutable State
or use another state management approach. When you use StatefulWidget
the instance of your State
class will be carried over to the "next frame" where new instance of your widget could use the state that's holding data from "previous frame" to render the UI.
References
Check out the links below if you're interested in learning more:
- https://api.flutter.dev/flutter/widgets/State/build.html
- https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
- https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html
- https://api.flutter.dev/flutter/widgets/State-class.html
CodePudding user response:
Stateless or stateful widgets all depends on your requirement. Taking your example, If your requirement is just to validate input text then your example is perfect. Think if you want to display the entered text in a Text widget, then you should move to stateful widget.
In summary if you want change the UI according user interaction then use a stateful widget and just to display things without handling changes use stateless widget like Text, Icon etc Official documentation
You may wonder then how the validator in you example displays error because it is inside a stateless widget. That's because TextFormField
is a statefulwidget and changes the UI according to the user interaction.
official documentation shows the form
in a stateful because in most cases forms need to be in stateful widget because it's need to modify the UIs according to user inputs. Form keys is a another major reason to use a stateful widget. Without a stateful widget it is hard to idenify a form uniquely in a widget tree using form keys.
CodePudding user response:
It should be stateful, as it's not only about TextFormField
, but we generally have others widget as well, which enable/disable
based on the TextFormField
contains values or not.
Example:
bool _btnEnabled = false;
TextFormField(
...
autovalidate: true,
validator: (String txt){
if (txt.length == 10){
setState((){
_btnEnabled = true;
});
} else {
setState((){
_btnEnabled = false;
});
}
}
...
FlatButton(
onPressed: _btnEnabled == true ? yourCallback : null,
child: ...