Gestire i form in Flutter con Form e TextFormField: validazione completa
GuidePrincipiante35 min Flutter 3.x

Gestire i form in Flutter con Form e TextFormField: validazione completa

Gestione dei form in Flutter

Quasi ogni applicazione richiede l'inserimento di dati da parte dell'utente: login, registrazione, ricerca, checkout. Flutter offre un sistema potente e dichiarativo per costruire form validati tramite i widget Form e TextFormField.

In questo tutorial costruiremo un form di registrazione completo con validazione di email, password e conferma password, gestendo lo stato tramite una GlobalKey<FormState>. Vedremo anche come mostrare messaggi di errore, abilitare/disabilitare il pulsante di invio e raccogliere i dati al submit.

Al termine avrai una solida base riutilizzabile in qualsiasi progetto.

  1. 1

    Preparare la struttura della schermata

    Creiamo uno StatefulWidget che ospiterà il nostro form. Usiamo uno Stateful perché dovremo gestire lo stato dei controller e della chiave del form.

    Partiamo da uno scheletro minimale con un Scaffold e un Padding.

    import 'package:flutter/material.dart';
    
    class RegistrationScreen extends StatefulWidget {
      const RegistrationScreen({super.key});
    
      @override
      State<RegistrationScreen> createState() => _RegistrationScreenState();
    }
    
    class _RegistrationScreenState extends State<RegistrationScreen> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Registrazione')),
          body: const Padding(
            padding: EdgeInsets.all(16),
            child: Text('Form in arrivo...'),
          ),
        );
      }
    }

    Risultato atteso

    Una schermata con AppBar 'Registrazione' e un testo segnaposto nel corpo.

  2. 2

    Aggiungere la GlobalKey e i controller

    Per gestire lo stato del form usiamo una GlobalKey<FormState>. Questa chiave ci permette di richiamare la validazione e il salvataggio da qualsiasi punto.

    Aggiungiamo inoltre i TextEditingController per leggere i valori dei campi, ricordandoci di rilasciarli nel metodo dispose.

    final _formKey = GlobalKey<FormState>();
    final _emailController = TextEditingController();
    final _passwordController = TextEditingController();
    final _confirmController = TextEditingController();
    
    @override
    void dispose() {
      _emailController.dispose();
      _passwordController.dispose();
      _confirmController.dispose();
      super.dispose();
    }

    Risultato atteso

    Lo state contiene la chiave del form e i tre controller pronti all'uso.

  3. 3

    Costruire il widget Form con i TextFormField

    Avvolgiamo i campi in un widget Form, associandogli la _formKey. Ogni campo è un TextFormField, che a differenza del normale TextField integra la logica di validazione tramite il parametro validator.

    Usiamo autovalidateMode: AutovalidateMode.onUserInteraction per mostrare gli errori solo dopo che l'utente ha iniziato a interagire.

    Form(
      key: _formKey,
      autovalidateMode: AutovalidateMode.onUserInteraction,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          TextFormField(
            controller: _emailController,
            decoration: const InputDecoration(labelText: 'Email'),
            keyboardType: TextInputType.emailAddress,
          ),
          const SizedBox(height: 12),
          TextFormField(
            controller: _passwordController,
            decoration: const InputDecoration(labelText: 'Password'),
            obscureText: true,
          ),
          const SizedBox(height: 12),
          TextFormField(
            controller: _confirmController,
            decoration: const InputDecoration(labelText: 'Conferma password'),
            obscureText: true,
          ),
        ],
      ),
    )

    Risultato atteso

    Tre campi di input visibili: email, password e conferma password.

  4. 4

    Scrivere i validatori

    Il parametro validator accetta una funzione che riceve il valore corrente e restituisce una String con il messaggio d'errore, oppure null se il valore è valido.

    Scriviamo una validazione per l'email tramite un'espressione regolare, una per la lunghezza minima della password e una per verificare che la conferma coincida.

    // Email
    validator: (value) {
      if (value == null || value.isEmpty) {
        return 'Inserisci una email';
      }
      final regex = RegExp(r'^[\w.+-]+@[\w-]+\.[\w.-]+$');
      if (!regex.hasMatch(value)) {
        return 'Email non valida';
      }
      return null;
    },
    
    // Password
    validator: (value) {
      if (value == null || value.length < 8) {
        return 'Almeno 8 caratteri';
      }
      return null;
    },
    
    // Conferma password
    validator: (value) {
      if (value != _passwordController.text) {
        return 'Le password non coincidono';
      }
      return null;
    },

    Risultato atteso

    Inserendo dati errati, sotto ogni campo compare il relativo messaggio di errore.

  5. 5

    Validare e inviare il form

    Aggiungiamo un ElevatedButton che richiama _formKey.currentState!.validate(). Questo metodo esegue tutti i validatori: restituisce true solo se tutti i campi sono validi.

    In caso positivo possiamo leggere i valori dai controller e procedere con la logica di registrazione (qui simulata con uno SnackBar).

    ElevatedButton(
      onPressed: _submit,
      child: const Text('Registrati'),
    )
    
    // ...
    
    void _submit() {
      if (_formKey.currentState!.validate()) {
        final email = _emailController.text.trim();
        // Qui invieresti i dati al backend
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Registrazione completata per $email')),
        );
      }
    }

    Risultato atteso

    Premendo 'Registrati' con dati validi appare uno SnackBar di conferma; con dati errati vengono mostrati gli errori.

  6. 6

    Migliorare la UX con focus e reset

    Per un'esperienza più fluida configuriamo textInputAction per spostare il focus al campo successivo e aggiungiamo un pulsante per resettare il form con _formKey.currentState!.reset().

    Questo è un esempio di best practice utile per form lunghi.

    // Sui primi campi
    TextFormField(
      controller: _emailController,
      textInputAction: TextInputAction.next,
      // ...
    ),
    
    // Sull'ultimo campo
    TextFormField(
      controller: _confirmController,
      textInputAction: TextInputAction.done,
      onFieldSubmitted: (_) => _submit(),
      // ...
    ),
    
    // Pulsante reset
    TextButton(
      onPressed: () => _formKey.currentState!.reset(),
      child: const Text('Pulisci'),
    )

    Risultato atteso

    Il tasto 'Invio' della tastiera passa al campo successivo e il pulsante 'Pulisci' azzera il form.

CondividiXLinkedInFacebookWhatsApp

Commenti (0)

Ancora nessun commento. Inizia tu!