Flutter TextField Widget

Flutter TextField widget is one of the most fundamental and widely used input widgets in Flutter app development. The Flutter TextField widget allows users to enter text input, making it essential for forms, search functionality, and user interactions. Whether you’re building a login form, registration page, or any app requiring user input, mastering the Flutter TextField widget properties and customization options is crucial for creating professional Flutter applications.

The Flutter TextField widget provides extensive customization capabilities, from basic text input to advanced features like input validation, text formatting, and interactive behaviors. Understanding how to implement and customize the Flutter TextField widget will significantly enhance your Flutter development skills and help you create more engaging user interfaces.

Understanding Flutter TextField Widget

The Flutter TextField widget is a stateful widget that creates a material design text input field. This Flutter TextField widget handles user text input and provides various properties to control its appearance, behavior, and functionality. The TextField widget in Flutter is highly customizable and supports different input types, decorations, and validation mechanisms.

TextField(
  decoration: InputDecoration(
    hintText: 'Enter your text here',
  ),
)

The basic Flutter TextField widget implementation requires minimal code but offers maximum flexibility for customization. You can control every aspect of the TextField widget appearance and behavior through its comprehensive property system.

Essential Flutter TextField Widget Properties

controller Property

The controller property in Flutter TextField widget manages the text being edited. The TextEditingController allows you to programmatically control the TextField widget content, retrieve current text, and listen to text changes.

class MyTextField extends StatefulWidget {
  @override
  _MyTextFieldState createState() => _MyTextFieldState();
}

class _MyTextFieldState extends State<MyTextField> {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      decoration: InputDecoration(
        labelText: 'Username',
      ),
    );
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

The controller property enables you to get and set text programmatically in your Flutter TextField widget. This is particularly useful when you need to clear the field, pre-populate data, or validate input before submission.

decoration Property

The decoration property transforms the Flutter TextField widget appearance using InputDecoration. This property controls borders, labels, hints, icons, and overall visual styling of your TextField widget.

TextField(
  decoration: InputDecoration(
    labelText: 'Email Address',
    hintText: 'Enter your email',
    prefixIcon: Icon(Icons.email),
    suffixIcon: Icon(Icons.visibility),
    border: OutlineInputBorder(
      borderRadius: BorderRadius.circular(10),
    ),
    filled: true,
    fillColor: Colors.grey[200],
  ),
)

The InputDecoration class provides numerous properties to customize your Flutter TextField widget appearance. You can add borders, colors, icons, and text styling to create visually appealing input fields that match your app’s design theme.

keyboardType Property

The keyboardType property in Flutter TextField widget determines which keyboard layout appears when users focus on the input field. Different keyboard types optimize user experience for specific input scenarios.

// Email keyboard
TextField(
  keyboardType: TextInputType.emailAddress,
  decoration: InputDecoration(labelText: 'Email'),
)

// Number keyboard
TextField(
  keyboardType: TextInputType.number,
  decoration: InputDecoration(labelText: 'Phone Number'),
)

// Multiline keyboard
TextField(
  keyboardType: TextInputType.multiline,
  maxLines: 4,
  decoration: InputDecoration(labelText: 'Description'),
)

The Flutter TextField widget supports various keyboard types including text, number, email, phone, datetime, multiline, and more. Choosing the appropriate keyboard type improves user experience and input accuracy.

obscureText Property

The obscureText property in Flutter TextField widget hides the entered text, making it perfect for password fields and sensitive information input. When enabled, the TextField widget displays dots or asterisks instead of actual characters.

class PasswordField extends StatefulWidget {
  @override
  _PasswordFieldState createState() => _PasswordFieldState();
}

class _PasswordFieldState extends State<PasswordField> {
  bool _obscureText = true;

  @override
  Widget build(BuildContext context) {
    return TextField(
      obscureText: _obscureText,
      decoration: InputDecoration(
        labelText: 'Password',
        suffixIcon: IconButton(
          icon: Icon(_obscureText ? Icons.visibility : Icons.visibility_off),
          onPressed: () {
            setState(() {
              _obscureText = !_obscureText;
            });
          },
        ),
      ),
    );
  }
}

The obscureText property enhances security in your Flutter TextField widget by hiding sensitive input. Combined with a visibility toggle, users can choose to reveal or hide their password while typing.

maxLength and maxLines Properties

The maxLength property in Flutter TextField widget limits the number of characters users can enter, while maxLines controls the height of the input field. These properties help maintain consistent UI layout and prevent excessive input.

// Single line with character limit
TextField(
  maxLength: 50,
  decoration: InputDecoration(
    labelText: 'Title',
    counterText: '', // Hides the counter
  ),
)

// Multi-line text area
TextField(
  maxLines: 5,
  maxLength: 500,
  decoration: InputDecoration(
    labelText: 'Comments',
    alignLabelWithHint: true,
    border: OutlineInputBorder(),
  ),
)

These properties give you precise control over Flutter TextField widget input constraints. The maxLength property prevents users from entering excessive text, while maxLines creates expandable text areas for longer content.

textInputAction Property

The textInputAction property in Flutter TextField widget customizes the action button on the software keyboard. This property improves navigation flow and user experience in forms with multiple TextField widgets.

TextField(
  textInputAction: TextInputAction.next,
  decoration: InputDecoration(labelText: 'First Name'),
  onSubmitted: (value) {
    // Move focus to next field
    FocusScope.of(context).nextFocus();
  },
)

TextField(
  textInputAction: TextInputAction.done,
  decoration: InputDecoration(labelText: 'Last Name'),
  onSubmitted: (value) {
    // Submit form or hide keyboard
    FocusScope.of(context).unfocus();
  },
)

The textInputAction property enhances form navigation in Flutter TextField widget implementations. Users can quickly move between fields or submit forms using keyboard actions, creating a smoother user experience.

Event Handling in Flutter TextField Widget

onChanged Callback

The onChanged callback in Flutter TextField widget executes whenever users modify the text content. This real-time callback enables input validation, search functionality, and dynamic UI updates.

class SearchField extends StatefulWidget {
  @override
  _SearchFieldState createState() => _SearchFieldState();
}

class _SearchFieldState extends State<SearchField> {
  String _searchText = '';
  List<String> _filteredResults = [];

  @override
  Widget build(BuildContext context) {
    return TextField(
      onChanged: (value) {
        setState(() {
          _searchText = value;
          _filteredResults = performSearch(value);
        });
      },
      decoration: InputDecoration(
        labelText: 'Search',
        prefixIcon: Icon(Icons.search),
      ),
    );
  }
  
  List<String> performSearch(String query) {
    // Implement search logic
    return [];
  }
}

The onChanged callback makes your Flutter TextField widget reactive and interactive. You can implement real-time search, input validation, or dynamic content filtering based on user input.

onSubmitted Callback

The onSubmitted callback in Flutter TextField widget triggers when users submit their input by pressing the action button on the keyboard. This callback is essential for form submission and navigation control.

TextField(
  onSubmitted: (value) {
    if (value.isNotEmpty) {
      // Process the submitted value
      processUserInput(value);
    }
  },
  decoration: InputDecoration(
    labelText: 'Enter command',
    suffixIcon: Icon(Icons.send),
  ),
)

The onSubmitted callback provides a clear user interaction point in your Flutter TextField widget. Users understand that pressing the keyboard action button will process their input.

Advanced Flutter TextField Widget Customization

Input Formatters

Input formatters in Flutter TextField widget control and modify user input in real-time. These formatters ensure data consistency and prevent invalid input patterns.

import 'package:flutter/services.dart';

TextField(
  inputFormatters: [
    FilteringTextInputFormatter.digitsOnly,
    LengthLimitingTextInputFormatter(10),
  ],
  keyboardType: TextInputType.number,
  decoration: InputDecoration(
    labelText: 'Phone Number',
    prefixText: '+1 ',
  ),
)

// Custom formatter for currency
class CurrencyInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  ) {
    String formatted = '\$${newValue.text}';
    return TextEditingValue(
      text: formatted,
      selection: TextSelection.collapsed(offset: formatted.length),
    );
  }
}

Input formatters enhance your Flutter TextField widget by ensuring data quality and consistency. You can use built-in formatters or create custom ones for specific input requirements.

Focus Management

Focus management in Flutter TextField widget controls which field receives keyboard input and how users navigate between multiple input fields. Proper focus handling improves form usability significantly.

class FocusDemo extends StatefulWidget {
  @override
  _FocusDemoState createState() => _FocusDemoState();
}

class _FocusDemoState extends State<FocusDemo> {
  final FocusNode _firstFieldFocus = FocusNode();
  final FocusNode _secondFieldFocus = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          focusNode: _firstFieldFocus,
          textInputAction: TextInputAction.next,
          onSubmitted: (value) {
            _secondFieldFocus.requestFocus();
          },
          decoration: InputDecoration(labelText: 'First Field'),
        ),
        TextField(
          focusNode: _secondFieldFocus,
          textInputAction: TextInputAction.done,
          decoration: InputDecoration(labelText: 'Second Field'),
        ),
      ],
    );
  }
  
  @override
  void dispose() {
    _firstFieldFocus.dispose();
    _secondFieldFocus.dispose();
    super.dispose();
  }
}

Focus management transforms your Flutter TextField widget forms into professional, user-friendly interfaces. Users can navigate smoothly between fields using keyboard actions.

Complete Flutter TextField Widget Example

Here’s a comprehensive example demonstrating multiple Flutter TextField widget properties and customizations in a registration form:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter TextField Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: RegistrationForm(),
    );
  }
}

class RegistrationForm extends StatefulWidget {
  @override
  _RegistrationFormState createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State<RegistrationForm> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final TextEditingController _bioController = TextEditingController();
  
  final FocusNode _nameFocus = FocusNode();
  final FocusNode _emailFocus = FocusNode();
  final FocusNode _phoneFocus = FocusNode();
  final FocusNode _passwordFocus = FocusNode();
  final FocusNode _bioFocus = FocusNode();
  
  bool _obscurePassword = true;
  String _searchText = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter TextField Widget Demo'),
        backgroundColor: Colors.blue[600],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                // Name TextField with basic decoration
                TextField(
                  controller: _nameController,
                  focusNode: _nameFocus,
                  textInputAction: TextInputAction.next,
                  keyboardType: TextInputType.text,
                  onSubmitted: (value) {
                    _emailFocus.requestFocus();
                  },
                  decoration: InputDecoration(
                    labelText: 'Full Name',
                    hintText: 'Enter your full name',
                    prefixIcon: Icon(Icons.person),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                    filled: true,
                    fillColor: Colors.grey[100],
                  ),
                ),
                SizedBox(height: 16),
                
                // Email TextField with email keyboard
                TextField(
                  controller: _emailController,
                  focusNode: _emailFocus,
                  textInputAction: TextInputAction.next,
                  keyboardType: TextInputType.emailAddress,
                  onSubmitted: (value) {
                    _phoneFocus.requestFocus();
                  },
                  decoration: InputDecoration(
                    labelText: 'Email Address',
                    hintText: 'your.email@example.com',
                    prefixIcon: Icon(Icons.email),
                    suffixIcon: Icon(Icons.alternate_email),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                    filled: true,
                    fillColor: Colors.blue[50],
                  ),
                ),
                SizedBox(height: 16),
                
                // Phone TextField with number keyboard and formatter
                TextField(
                  controller: _phoneController,
                  focusNode: _phoneFocus,
                  textInputAction: TextInputAction.next,
                  keyboardType: TextInputType.phone,
                  inputFormatters: [
                    FilteringTextInputFormatter.digitsOnly,
                    LengthLimitingTextInputFormatter(10),
                  ],
                  onSubmitted: (value) {
                    _passwordFocus.requestFocus();
                  },
                  decoration: InputDecoration(
                    labelText: 'Phone Number',
                    hintText: '1234567890',
                    prefixIcon: Icon(Icons.phone),
                    prefixText: '+1 ',
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                    filled: true,
                    fillColor: Colors.green[50],
                  ),
                ),
                SizedBox(height: 16),
                
                // Password TextField with obscure text and toggle
                TextField(
                  controller: _passwordController,
                  focusNode: _passwordFocus,
                  textInputAction: TextInputAction.next,
                  obscureText: _obscurePassword,
                  onSubmitted: (value) {
                    _bioFocus.requestFocus();
                  },
                  decoration: InputDecoration(
                    labelText: 'Password',
                    hintText: 'Enter secure password',
                    prefixIcon: Icon(Icons.lock),
                    suffixIcon: IconButton(
                      icon: Icon(_obscurePassword 
                          ? Icons.visibility 
                          : Icons.visibility_off),
                      onPressed: () {
                        setState(() {
                          _obscurePassword = !_obscurePassword;
                        });
                      },
                    ),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                    filled: true,
                    fillColor: Colors.red[50],
                  ),
                ),
                SizedBox(height: 16),
                
                // Multi-line bio TextField
                TextField(
                  controller: _bioController,
                  focusNode: _bioFocus,
                  textInputAction: TextInputAction.newline,
                  keyboardType: TextInputType.multiline,
                  maxLines: 4,
                  maxLength: 200,
                  decoration: InputDecoration(
                    labelText: 'Bio',
                    hintText: 'Tell us about yourself...',
                    prefixIcon: Icon(Icons.description),
                    alignLabelWithHint: true,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                    filled: true,
                    fillColor: Colors.purple[50],
                  ),
                ),
                SizedBox(height: 16),
                
                // Search TextField with onChanged
                TextField(
                  onChanged: (value) {
                    setState(() {
                      _searchText = value;
                    });
                  },
                  decoration: InputDecoration(
                    labelText: 'Search',
                    hintText: 'Search for something...',
                    prefixIcon: Icon(Icons.search),
                    suffixIcon: _searchText.isNotEmpty
                        ? IconButton(
                            icon: Icon(Icons.clear),
                            onPressed: () {
                              setState(() {
                                _searchText = '';
                              });
                            },
                          )
                        : null,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                    filled: true,
                    fillColor: Colors.orange[50],
                  ),
                ),
                SizedBox(height: 20),
                
                if (_searchText.isNotEmpty)
                  Container(
                    padding: EdgeInsets.all(12),
                    decoration: BoxDecoration(
                      color: Colors.grey[200],
                      borderRadius: BorderRadius.circular(8),
                    ),
                    child: Text(
                      'Searching for: $_searchText',
                      style: TextStyle(
                        fontSize: 16,
                        fontStyle: FontStyle.italic,
                      ),
                    ),
                  ),
                
                SizedBox(height: 32),
                
                // Submit button
                ElevatedButton(
                  onPressed: () {
                    _submitForm();
                  },
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.blue[600],
                    padding: EdgeInsets.symmetric(vertical: 16),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10),
                    ),
                  ),
                  child: Text(
                    'Register',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
  
  void _submitForm() {
    String name = _nameController.text;
    String email = _emailController.text;
    String phone = _phoneController.text;
    String password = _passwordController.text;
    String bio = _bioController.text;
    
    if (name.isNotEmpty && email.isNotEmpty && password.isNotEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Registration submitted successfully!'),
          backgroundColor: Colors.green,
        ),
      );
      
      // Clear all fields
      _nameController.clear();
      _emailController.clear();
      _phoneController.clear();
      _passwordController.clear();
      _bioController.clear();
      
      // Hide keyboard
      FocusScope.of(context).unfocus();
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Please fill in all required fields'),
          backgroundColor: Colors.red,
        ),
      );
    }
  }
  
  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    _phoneController.dispose();
    _passwordController.dispose();
    _bioController.dispose();
    _nameFocus.dispose();
    _emailFocus.dispose();
    _phoneFocus.dispose();
    _passwordFocus.dispose();
    _bioFocus.dispose();
    super.dispose();
  }
}

This comprehensive Flutter TextField widget example demonstrates practical implementation of multiple properties and features. The registration form showcases different keyboard types, input formatters, focus management, decoration options, and event handling. Each TextField widget serves a specific purpose while maintaining consistent styling and user experience.

The example includes proper resource management by disposing controllers and focus nodes, preventing memory leaks in your Flutter application. You can customize this Flutter TextField widget implementation further by adding validation, custom themes, or integration with state management solutions like Provider or Bloc.