How to Use TextFormField in Flutter: Syntax and Example
Use
TextFormField in Flutter to create input fields inside a Form widget. It supports validation, saving, and styling. Wrap it in a Form and use a GlobalKey to manage form state.Syntax
The TextFormField widget is used inside a Form widget to collect user input. Key parts include:
- controller: manages the text being edited.
- decoration: adds styling like labels and borders.
- validator: a function to check input validity.
- onSaved: saves the input value when the form is submitted.
dart
Form(
key: _formKey,
child: TextFormField(
controller: _controller,
decoration: InputDecoration(labelText: 'Enter text'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
_savedText = value ?? '';
},
),
)Example
This example shows a simple form with a TextFormField that validates input and saves it when a button is pressed.
dart
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('TextFormField Example')), body: Padding( padding: EdgeInsets.all(16), child: MyCustomForm(), ), ), ); } } class MyCustomForm extends StatefulWidget { @override _MyCustomFormState createState() => _MyCustomFormState(); } class _MyCustomFormState extends State<MyCustomForm> { final _formKey = GlobalKey<FormState>(); final _controller = TextEditingController(); String _savedText = ''; @override Widget build(BuildContext context) { return Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: _controller, decoration: InputDecoration(labelText: 'Enter your name'), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter some text'; } return null; }, onSaved: (value) { _savedText = value ?? ''; }, ), SizedBox(height: 20), ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Hello, $_savedText!')), ); } }, child: Text('Submit'), ), ], ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
Output
A screen with a text input labeled 'Enter your name' and a 'Submit' button. If input is empty and Submit is pressed, an error message appears below the field. If valid, a message 'Hello, [input]!' shows at the bottom.
Common Pitfalls
- Not wrapping
TextFormFieldinside aFormwidget, so validation and saving won't work. - Forgetting to assign a
GlobalKeyto theFormto access its state. - Not disposing the
TextEditingControllerin stateful widgets, causing memory leaks. - Using
TextFieldinstead ofTextFormFieldwhen validation is needed.
dart
/* Wrong: No Form widget */ TextFormField( validator: (value) => value!.isEmpty ? 'Enter text' : null, ), /* Right: Wrapped in Form with GlobalKey */ final _formKey = GlobalKey<FormState>(); Form( key: _formKey, child: TextFormField( validator: (value) => value!.isEmpty ? 'Enter text' : null, ), )
Quick Reference
TextFormField Quick Tips:
- Use
controllerto read or set text programmatically. - Use
validatorto check input and return error messages. - Call
_formKey.currentState!.validate()to trigger validation. - Call
_formKey.currentState!.save()to save input values. - Dispose controllers in
dispose()method to free resources.
Key Takeaways
Always wrap TextFormField inside a Form widget with a GlobalKey to manage validation and saving.
Use the validator function to check user input and show error messages.
Use a TextEditingController to read or modify the text input programmatically.
Dispose of TextEditingController in stateful widgets to avoid memory leaks.
Call validate() and save() on the FormState to process form input correctly.