Condividere dati tra schermate in Flutter con Navigator: risultati e argomenti

Foto di serjan midili su Unsplash

GuidePrincipiante30 min Flutter 3.x

Condividere dati tra schermate in Flutter con Navigator: risultati e argomenti

Quando un'app cresce, le schermate devono scambiarsi informazioni: un elenco apre un dettaglio, una pagina di scelta restituisce il valore selezionato.

In questo tutorial beginner vedremo come:

  • passare argomenti a una schermata tramite il costruttore;
  • ricevere un risultato quando la schermata si chiude;
  • gestire il caso in cui l'utente annulla (risultato null).

Serve solo un progetto Flutter vuoto creato con flutter create.

  1. 1

    Preparare l'app e la schermata iniziale

    Sostituisci il contenuto di lib/main.dart con una MaterialApp e una HomePage che mostra il nome di un profilo utente e un pulsante per modificarlo.

    Per ora il pulsante non fa nulla: lo collegheremo nei passi successivi.

    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Navigator Demo',
          theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key});
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      String _nome = 'Mario Rossi';
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Profilo')),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(_nome, style: Theme.of(context).textTheme.headlineSmall),
                const SizedBox(height: 16),
                FilledButton(
                  onPressed: () {},
                  child: const Text('Modifica nome'),
                ),
              ],
            ),
          ),
        );
      }
    }

    Risultato atteso

    L'app si avvia e mostra il nome 'Mario Rossi' con un pulsante ancora inattivo.

  2. 2

    Creare la seconda schermata che riceve un argomento

    La seconda schermata riceve il nome attuale tramite il costruttore: è il modo più semplice e sicuro (tipizzato) per passare dati con Navigator.

    Crea il file lib/edit_page.dart con un TextField precompilato.

    import 'package:flutter/material.dart';
    
    class EditPage extends StatefulWidget {
      const EditPage({super.key, required this.nomeIniziale});
    
      final String nomeIniziale;
    
      @override
      State<EditPage> createState() => _EditPageState();
    }
    
    class _EditPageState extends State<EditPage> {
      late final TextEditingController _controller =
          TextEditingController(text: widget.nomeIniziale);
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Modifica nome')),
          body: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                TextField(
                  controller: _controller,
                  autofocus: true,
                  decoration: const InputDecoration(
                    labelText: 'Nome',
                    border: OutlineInputBorder(),
                  ),
                ),
                const SizedBox(height: 16),
                FilledButton(
                  onPressed: () {},
                  child: const Text('Salva'),
                ),
              ],
            ),
          ),
        );
      }
    }

    Risultato atteso

    Il file compila senza errori: la schermata mostra un campo di testo precompilato.

  3. 3

    Aprire la schermata con Navigator.push

    Da HomePage apriamo EditPage passando il nome attuale. MaterialPageRoute è tipizzato con <String> perché ci aspettiamo una stringa come risultato.

    Ricorda di importare edit_page.dart in main.dart.

    import 'edit_page.dart';
    
    // dentro _HomePageState
    Future<void> _apriModifica() async {
      await Navigator.push<String>(
        context,
        MaterialPageRoute<String>(
          builder: (context) => EditPage(nomeIniziale: _nome),
        ),
      );
    }
    
    // collega il pulsante:
    // FilledButton(onPressed: _apriModifica, child: const Text('Modifica nome')),

    Risultato atteso

    Toccando il pulsante si apre la seconda schermata con il nome già inserito nel campo.

  4. 4

    Restituire un risultato con Navigator.pop

    Per restituire un valore alla schermata precedente basta passarlo come secondo argomento di Navigator.pop.

    Aggiungiamo anche un controllo: se il campo è vuoto non chiudiamo la pagina.

    FilledButton(
      onPressed: () {
        final testo = _controller.text.trim();
        if (testo.isEmpty) {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Il nome non può essere vuoto')),
          );
          return;
        }
        Navigator.pop(context, testo);
      },
      child: const Text('Salva'),
    ),

    Risultato atteso

    Premendo 'Salva' la schermata si chiude; con il campo vuoto appare invece una SnackBar.

  5. 5

    Ricevere il risultato con await e aggiornare la UI

    Navigator.push restituisce un Future che si completa quando la rotta viene chiusa. Il valore può essere null se l'utente torna indietro con la freccia o con il gesto di sistema: va sempre gestito.

    Usa anche context.mounted prima di toccare il context dopo un await.

    Future<void> _apriModifica() async {
      final risultato = await Navigator.push<String>(
        context,
        MaterialPageRoute<String>(
          builder: (context) => EditPage(nomeIniziale: _nome),
        ),
      );
    
      if (!context.mounted) return;
    
      if (risultato == null) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Modifica annullata')),
        );
        return;
      }
    
      setState(() => _nome = risultato);
    }

    Risultato atteso

    Salvando, il nome in home si aggiorna; tornando indietro senza salvare compare 'Modifica annullata'.

  6. 6

    Passare oggetti complessi, non solo stringhe

    Lo stesso schema funziona con qualunque tipo: definisci una semplice classe e usala come parametro del costruttore e come tipo di ritorno.

    È la base per un flusso lista → dettaglio → modifica.

    class Profilo {
      const Profilo({required this.nome, required this.eta});
      final String nome;
      final int eta;
    }
    
    // apertura
    final nuovo = await Navigator.push<Profilo>(
      context,
      MaterialPageRoute<Profilo>(
        builder: (_) => EditProfiloPage(profilo: profiloAttuale),
      ),
    );
    
    // chiusura nella seconda schermata
    // Navigator.pop(context, Profilo(nome: 'Anna', eta: 30));

    Risultato atteso

    Puoi scambiare interi oggetti tra schermate mantenendo il type-safety del compilatore.

CondividiXLinkedInFacebookWhatsApp

Commenti (0)

Ancora nessun commento. Inizia tu!