Using TextFormField for Input in Flutter: A Beginner's Guide

The TextFormField widget is a fundamental building block for creating forms and handling user input in Flutter. It combines a text input field with built-in validation, making it ideal for scenarios like login forms, registration forms, or search bars.

This guide will help you understand how to use TextFormField effectively and implement features like validation and customization.

What is TextFormField?

The TextFormField widget is a more advanced version of the basic TextField. It provides:

  • Validation: Built-in support for input validation using the validator property.
  • Integration with Forms: Works seamlessly with the Form widget to manage state and validation.
  • Customizable Input: Supports features like labels, hints, icons, and keyboard types.

Basic Example of TextFormField

Here’s a simple example of a TextFormField to accept a user's name:

TextFormField(
  decoration: InputDecoration(
    labelText: 'Enter your name',
    border: OutlineInputBorder(),
  ),
);

Key Features:

  1. Label: The labelText displays a hint to users.
  2. Border: The OutlineInputBorder gives a framed appearance to the input field.

Adding Validation

Validation ensures that users provide the correct input. Use the validator property to define validation logic.

Example: Validating Non-Empty Input

TextFormField(
  decoration: InputDecoration(
    labelText: 'Email',
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'This field cannot be empty';
    }
    return null;
  },
);

Example: Validating Email Format

TextFormField(
 decoration: InputDecoration(
   labelText: 'Email',
 ),
 keyboardType: TextInputType.emailAddress,
 validator: (value) {
   if (value == null || value.isEmpty) {
     return 'Please enter an email';
   }
   if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
     return 'Enter a valid email address';
   }
   return null;
 },
);

Managing Input with Controllers

To retrieve or modify the input programmatically, use a TextEditingController.

Example: Accessing Input Value

final TextEditingController _controller = TextEditingController();

TextFormField(
  controller: _controller,
  decoration: InputDecoration(labelText: 'Username'),
);

ElevatedButton(
  onPressed: () {
    print('Input value: ${_controller.text}');
  },
  child: Text('Submit'),
);

Customizing Appearance

The InputDecoration property allows you to customize the appearance of the TextFormField.

Example: Adding Icons and Styling

TextFormField(
  decoration: InputDecoration(
    labelText: 'Password',
    hintText: 'Enter your password',
    prefixIcon: Icon(Icons.lock),
    border: OutlineInputBorder(),
  ),
  obscureText: true, // Hides input for passwords
);

Custom Styling

You can style the field further with TextStyle and themes:

TextFormField(
  decoration: InputDecoration(
    labelText: 'Custom Styled Input',
    labelStyle: TextStyle(color: Colors.blue, fontSize: 16),
    enabledBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Colors.blue),
    ),
    focusedBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Colors.green, width: 2),
    ),
  ),
);

Handling Focus with FocusNode

The FocusNode lets you control and listen to focus changes.

Example: Moving Focus Programmatically

final FocusNode _focusNode1 = FocusNode();
final FocusNode _focusNode2 = FocusNode();

TextFormField(
  focusNode: _focusNode1,
  decoration: InputDecoration(labelText: 'Field 1'),
  onFieldSubmitted: (_) {
    FocusScope.of(context).requestFocus(_focusNode2);
  },
);

TextFormField(
  focusNode: _focusNode2,
  decoration: InputDecoration(labelText: 'Field 2'),
);

Example: Login Form with TextFormField

Here’s a complete example using TextFormField for a login form:

import 'package:flutter/material.dart';

class LoginForm extends StatefulWidget {
  @override
  _LoginFormState createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  void _submitForm() {
    if (_formKey.currentState!.validate()) {
      print('Email: ${_emailController.text}');
      print('Password: ${_passwordController.text}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login Form')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: _emailController,
                decoration: InputDecoration(labelText: 'Email'),
                keyboardType: TextInputType.emailAddress,
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter your email';
                  }
                  if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
                    return 'Enter a valid email address';
                  }
                  return null;
                },
              ),
              SizedBox(height: 16),
              TextFormField(
                controller: _passwordController,
                decoration: InputDecoration(labelText: 'Password'),
                obscureText: true,
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter your password';
                  }
                  if (value.length < 6) {
                    return 'Password must be at least 6 characters long';
                  }
                  return null;
                },
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: _submitForm,
                child: Text('Login'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Tips for Using TextFormField Effectively

  1. Use Controllers for Programmatic Input Management: Easily retrieve and manipulate text values.
  2. Leverage Built-In Validation: The validator property simplifies error handling.
  3. Customize Input for Specific Use Cases: Use keyboardType for different input types like emails, numbers, or passwords.
  4. Focus Management: Use FocusNode to enhance user navigation between fields.

With these insights, you’re ready to incorporate TextFormField into your Flutter projects for a seamless input experience. Experiment with its customization options and validation capabilities to build intuitive user forms!