0
0
FlutterHow-ToBeginner · 3 min read

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 TextFormField inside a Form widget, so validation and saving won't work.
  • Forgetting to assign a GlobalKey to the Form to access its state.
  • Not disposing the TextEditingController in stateful widgets, causing memory leaks.
  • Using TextField instead of TextFormField when 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 controller to read or set text programmatically.
  • Use validator to 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.