Flutter Form Validation: solved exercise with TextFormField

  2 minutes

If you are looking for Flutter form validation, this solved exercise gives you a practical implementation pattern you can reuse in real projects.

Build a screen with:

  • email field
  • password field
  • field validation rules
  • submit button with user feedback
import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: LoginFormPage()));

class LoginFormPage extends StatefulWidget {
  const LoginFormPage({super.key});

  @override
  State<LoginFormPage> createState() => _LoginFormPageState();
}

class _LoginFormPageState extends State<LoginFormPage> {
  final _formKey = GlobalKey<FormState>();
  final _emailCtrl = TextEditingController();
  final _passCtrl = TextEditingController();

  @override
  void dispose() {
    _emailCtrl.dispose();
    _passCtrl.dispose();
    super.dispose();
  }

  void submit() {
    if (_formKey.currentState!.validate()) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Formulario valido: ${_emailCtrl.text}')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Formulario Flutter')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: _emailCtrl,
                decoration: const InputDecoration(labelText: 'Email'),
                validator: (value) {
                  if (value == null || value.isEmpty) return 'Email obligatorio';
                  if (!value.contains('@')) return 'Email no valido';
                  return null;
                },
              ),
              TextFormField(
                controller: _passCtrl,
                decoration: const InputDecoration(labelText: 'Password'),
                obscureText: true,
                validator: (value) {
                  if (value == null || value.length < 6) return 'Minimo 6 caracteres';
                  return null;
                },
              ),
              const SizedBox(height: 16),
              ElevatedButton(onPressed: submit, child: const Text('Enviar')),
            ],
          ),
        ),
      ),
    );
  }
}

The form submits only when all validation rules are satisfied.

  • Not using GlobalKey<FormState>.
  • Triggering validation on every keystroke without strategy.
  • Forgetting to dispose controllers.

This pattern is the base for login, signup, checkout, and internal business forms.

Form with TextFormField and per-field validators.

Inside field validators with clear and short messages.

Yes. Extract validators and field widgets as the screen grows.