TextField in Flutter
Master user input capture in your applications
Download Complete ExamplesWhat is TextField?
The TextField is one of the most important widgets in Flutter for capturing user text input. It's essential for forms, searches, login and any interaction that requires text.
Main Features:
- ✅ Simple and secure text capture
- ✅ Validation and error handling
- ✅ Different keyboard types
- ✅ Full decoration and customization
- ✅ Integration with Form and FormField
Basic Syntax
Minimum TextField
TextField(
onChanged: (text) {
print('Text changed: $text');
},
)
TextField with Controller
final TextEditingController _controller = TextEditingController();
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Name',
hintText: 'Type your name',
border: OutlineInputBorder(),
),
)
Main Properties
controller
Controls the TextField's text and state
Type: TextEditingController
Required: No
decoration
Customizes the TextField's appearance
Type: InputDecoration
Required: No
onChanged
Function called when the text changes
Type: ValueChanged<String>
Required: No
keyboardType
Type of keyboard to show
Type: TextInputType
Default: TextInputType.text
obscureText
Hides the text (for passwords)
Type: bool
Default: false
maxLines
Maximum number of lines
Type: int
Default: 1
InputDecoration
The decoration property lets you fully customize the TextField's appearance.
labelText
Text that appears as a label
hintText
Hint text shown when empty
prefixIcon / suffixIcon
Icons before/after the text
errorText
Error text shown during validation
Complete Decoration Example:
TextField(
decoration: InputDecoration(
labelText: 'Email',
hintText: 'tu@email.com',
prefixIcon: Icon(Icons.email),
suffixIcon: Icon(Icons.check_circle, color: Colors.green),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2),
),
errorText: _emailError,
errorStyle: TextStyle(color: Colors.red),
),
)
Types of TextField
1. TextField Basics
TextField()
2. With Outline Border
border: OutlineInputBorder()
3. With Icon
prefixIcon: Icon(Icons.search)
4. For Password
obscureText: true
5. Multiline
maxLines: 5
6. With Validation
errorText: 'Campo requerido'
State Management
1. TextEditingController
For advanced control of the text and state
class _MyFormState extends State<MyForm> {
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
_controller.addListener(_onTextChanged);
}
void _onTextChanged() {
print('Texto actual: ${_controller.text}');
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
decoration: InputDecoration(labelText: 'Name'),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
2. onChanged with setState
For simple cases with no need for a controller
class _MyFormState extends State<MyForm> {
String _text = '';
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
onChanged: (text) {
setState(() {
_text = text;
});
},
decoration: InputDecoration(labelText: 'Type something'),
),
Text('Has escrito: $_text'),
],
);
}
}
Keyboard Types
TextInputType.text
Standard text keyboard
keyboardType: TextInputType.text
TextInputType.emailAddress
Keyboard optimized for emails
keyboardType: TextInputType.emailAddress
TextInputType.phone
Numeric keyboard for phones
keyboardType: TextInputType.phone
TextInputType.number
Standard numeric keyboard
keyboardType: TextInputType.number
TextInputType.multiline
Optimized for multiline text
keyboardType: TextInputType.multiline
Customizer for TextField
// El código se generará aquí
Form Validation
Validation with Form
Using Form and TextFormField for built-in validation
final _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingresa tu nombre';
}
return null;
},
decoration: InputDecoration(labelText: 'Name'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Formulario válido }
},
child: Text('Send'),
),
],
),
)
Custom Validation
Specific validations for different cases
String? _validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'El email es requerido';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return 'Ingresa un email válido';
}
return null;
}
TextFormField(
validator: _validateEmail,
decoration: InputDecoration(labelText: 'Email'),
)
Best Practices
✅ What you SHOULD do
- Use TextEditingController for important fields
- Implement validation on all form fields
- Use the appropriate keyboardType for each field
- Provide clear hintText and labelText
- Handle controller disposal
❌ What you should NOT do
- Don't forget to clean up controllers
- Don't use onChanged for complex real-time validation
- Don't ignore accessibility (semanticLabel)
- Don't use obscureText for fields that aren't passwords
- Don't forget error handling
Pro Tip
For complex forms, consider using packages like flutter_form_builder or reactive_forms that provide more advanced validation and state management.
Exercises to Practice
Exercise 1: Registration Form
Create a registration form with validation for name, email and password.
Fields: Name (required), Email (validation), Password (minimum 6 characters)
Exercise 2: Real-Time Search
Implement a search field that filters a list as the user types.
Hint: Use onChanged with debounce to optimize
Exercise 3: Contact Form
Create a contact form with fields for name, email, subject and a multiline message.
Fields: Use TextFormField with custom validation
Common Troubleshooting
❌ The text doesn't update
Solution: Make sure to call setState() or use a controller
setState(() { _text = newValue; });
❌ Error with the controller
Solution: Don't forget to call dispose() on the controller
@override
void dispose() {
_controller.dispose();
super.dispose();
}
❌ The keyboard doesn't show @ for emails
Solution: Use TextInputType.emailAddress
keyboardType: TextInputType.emailAddress