TextField in Flutter

Master user input capture in your applications

Download Complete Examples

What 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

Type your name

prefixIcon / suffixIcon

Icons before/after the text

Text

errorText

Error text shown during validation

Texto inválido
Este campo es requerido

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

Este campo es obligatorio
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

ABC
keyboardType: TextInputType.text

TextInputType.emailAddress

Keyboard optimized for emails

@ .com
keyboardType: TextInputType.emailAddress

TextInputType.phone

Numeric keyboard for phones

123
keyboardType: TextInputType.phone

TextInputType.number

Standard numeric keyboard

123
keyboardType: TextInputType.number

TextInputType.multiline

Optimized for multiline text

keyboardType: TextInputType.multiline

Customizer for TextField

Texto actual:
// 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