[{"data":1,"prerenderedAt":85},["ShallowReactive",2],{"tutorial-navigazione-di-base-in-flutter-con-navigator-push-pop-e-passaggio-dati":3,"comments-tutorial-navigazione-di-base-in-flutter-con-navigator-push-pop-e-passaggio-dati":84},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"cover_remote_url":10,"cover_credit":11,"video_url":15,"video":16,"difficulty":20,"estimated_minutes":21,"flutter_version":22,"status":23,"published_at":24,"meta_title":25,"meta_description":26,"category":27,"author":31,"steps":33},75,"Navigazione di base in Flutter con Navigator: push, pop e passaggio dati","navigazione-di-base-in-flutter-con-navigator-push-pop-e-passaggio-dati","Impara a spostarti tra le schermate di un'app Flutter usando Navigator.push e Navigator.pop, passando dati avanti e indietro e usando le rotte con nome.","Quasi ogni app ha più di una schermata: una lista di prodotti che apre il dettaglio, un profilo che apre le impostazioni, un pulsante che porta a un form.\n\nIn Flutter la navigazione è gestita dal widget **Navigator**, che mantiene uno *stack* (una pila) di schermate: `push` mette una nuova schermata sopra la pila, `pop` la toglie e torna a quella precedente.\n\nIn questa guida per principianti vedrai:\n\n- come aprire una nuova schermata con `Navigator.push` e `MaterialPageRoute`;\n- come tornare indietro con `Navigator.pop` (e cosa fa il tasto back di Android);\n- come **passare dati** alla schermata di destinazione;\n- come **ricevere un risultato** dalla schermata aperta;\n- come usare le **rotte con nome** (`routes` e `pushNamed`) per progetti più ordinati.\n\n> Non servono pacchetti esterni: tutto quello che useremo è già incluso in Flutter. Se in futuro ti servirà una navigazione più avanzata (deep link, URL sul web) potrai passare a `go_router`, ma i concetti di questa guida restano validi.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F84a29c59-5450-423b-818a-944dcad598cd.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1569003501619-5d9351a30fd9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODc3MTg2ODF8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Marjan Blan","https:\u002F\u002Funsplash.com\u002F@marjan_blan","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fgray-wooden-door-that-is-close-5JFbwrVKHPs","https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=VyBH4CsJhno",{"youtube_id":17,"duration_seconds":18,"published_at":19},"VyBH4CsJhno",126,"2026-08-26T05:32:29+00:00","beginner",30,"3.x","published","2026-08-26T04:31:22+00:00","Navigator in Flutter: push, pop e passaggio dati","Guida beginner alla navigazione in Flutter: apri schermate con Navigator.push, torna indietro con pop, passa dati e usa le rotte con nome.",{"id":28,"name":29,"slug":30},1,"Guide","guide",{"id":28,"name":32},"Flutter Bot",[34,42,49,56,63,70,77],{"id":35,"position":28,"title":36,"body":37,"code_snippet":38,"code_language":39,"expected_result":40,"demo_url":41,"video_url":41},500,"Preparare il progetto e la prima schermata","Crea un nuovo progetto (o parti da uno esistente) e svuota il file `lib\u002Fmain.dart`.\n\nPartiamo da una **HomeScreen** molto semplice, con uno `Scaffold`, una `AppBar` e un pulsante centrale che per ora non fa nulla.\n\nPunti chiave:\n\n- `MaterialApp` contiene già al suo interno un `Navigator`: è questo che ci permette di usare `Navigator.push` in qualsiasi punto dell'app;\n- `home:` indica la **prima rotta** della pila, quella che l'utente vede all'avvio.\n\nAvvia l'app con `flutter run`: dovresti vedere la home con il pulsante.","import 'package:flutter\u002Fmaterial.dart';\n\nvoid main() => runApp(const MyApp());\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Navigazione base',\n      theme: ThemeData(\n        colorSchemeSeed: Colors.indigo,\n        useMaterial3: true,\n      ),\n      home: const HomeScreen(),\n    );\n  }\n}\n\nclass HomeScreen extends StatelessWidget {\n  const HomeScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Home')),\n      body: Center(\n        child: FilledButton(\n          onPressed: () {\n            \u002F\u002F Qui apriremo la seconda schermata\n          },\n          child: const Text('Vai al dettaglio'),\n        ),\n      ),\n    );\n  }\n}","dart","L'app si avvia mostrando la schermata Home con un pulsante \"Vai al dettaglio\" al centro, che al tocco non fa ancora nulla.",null,{"id":43,"position":44,"title":45,"body":46,"code_snippet":47,"code_language":39,"expected_result":48,"demo_url":41,"video_url":41},501,2,"Aprire una seconda schermata con Navigator.push","Creiamo una seconda schermata, `DettaglioScreen`, e apriamola con `Navigator.push`.\n\n```dart\nNavigator.push(\n  context,\n  MaterialPageRoute(builder: (context) => const DettaglioScreen()),\n);\n```\n\nCosa succede:\n\n- `context` serve a Flutter per trovare il `Navigator` più vicino risalendo l'albero dei widget;\n- `MaterialPageRoute` descrive la nuova rotta e fornisce l'**animazione di transizione** tipica della piattaforma (slide su iOS, fade\u002Fscale su Android);\n- il parametro `builder` costruisce la schermata **solo quando serve**.\n\nNota che nella `AppBar` della seconda schermata compare automaticamente la freccia \"indietro\": Flutter la aggiunge da solo quando c'è una rotta sottostante nella pila.","class HomeScreen extends StatelessWidget {\n  const HomeScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Home')),\n      body: Center(\n        child: FilledButton(\n          onPressed: () {\n            Navigator.push(\n              context,\n              MaterialPageRoute(\n                builder: (context) => const DettaglioScreen(),\n              ),\n            );\n          },\n          child: const Text('Vai al dettaglio'),\n        ),\n      ),\n    );\n  }\n}\n\nclass DettaglioScreen extends StatelessWidget {\n  const DettaglioScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Dettaglio')),\n      body: const Center(\n        child: Text('Sono la seconda schermata'),\n      ),\n    );\n  }\n}","Toccando il pulsante si apre la schermata Dettaglio con animazione di transizione e freccia \"indietro\" nella AppBar.",{"id":50,"position":51,"title":52,"body":53,"code_snippet":54,"code_language":39,"expected_result":55,"demo_url":41,"video_url":41},502,3,"Tornare indietro con Navigator.pop","Il tasto back di Android e la freccia della `AppBar` chiamano già `Navigator.pop` per te. Ma spesso serve un pulsante esplicito, ad esempio \"Annulla\" o \"Chiudi\".\n\n```dart\nNavigator.pop(context);\n```\n\n`pop` rimuove la rotta corrente dalla pila e mostra quella sotto.\n\n**Errore tipico da principiante:** chiamare `Navigator.pop(context)` sulla prima schermata. La pila resta vuota e su Android l'app si chiude (su iOS non succede nulla). Puoi verificare se è possibile tornare indietro con `Navigator.canPop(context)`.","class DettaglioScreen extends StatelessWidget {\n  const DettaglioScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Dettaglio')),\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            const Text('Sono la seconda schermata'),\n            const SizedBox(height: 16),\n            OutlinedButton(\n              onPressed: () {\n                if (Navigator.canPop(context)) {\n                  Navigator.pop(context);\n                }\n              },\n              child: const Text('Torna indietro'),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}","Il pulsante \"Torna indietro\" riporta alla Home esattamente come la freccia della AppBar.",{"id":57,"position":58,"title":59,"body":60,"code_snippet":61,"code_language":39,"expected_result":62,"demo_url":41,"video_url":41},503,4,"Passare dati alla schermata di destinazione","Il modo più semplice e sicuro per passare dati è tramite il **costruttore** del widget di destinazione: niente stringhe magiche, tutto controllato dal compilatore.\n\nCreiamo un piccolo modello `Prodotto` e una lista nella Home: toccando un elemento apriamo il dettaglio passando l'oggetto selezionato.\n\nOsserva:\n\n- il costruttore di `DettaglioScreen` riceve un parametro `required this.prodotto`;\n- il campo è `final`, quindi immutabile;\n- non c'è nessun cast o controllo di tipo da fare: se sbagli tipo, l'errore esce in fase di compilazione.","class Prodotto {\n  final String nome;\n  final double prezzo;\n\n  const Prodotto(this.nome, this.prezzo);\n}\n\nconst prodotti = \u003CProdotto>[\n  Prodotto('Tastiera meccanica', 89.90),\n  Prodotto('Mouse wireless', 34.50),\n  Prodotto('Monitor 27\"', 219.00),\n];\n\nclass HomeScreen extends StatelessWidget {\n  const HomeScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Prodotti')),\n      body: ListView.builder(\n        itemCount: prodotti.length,\n        itemBuilder: (context, index) {\n          final prodotto = prodotti[index];\n          return ListTile(\n            title: Text(prodotto.nome),\n            subtitle: Text('${prodotto.prezzo.toStringAsFixed(2)} €'),\n            trailing: const Icon(Icons.chevron_right),\n            onTap: () {\n              Navigator.push(\n                context,\n                MaterialPageRoute(\n                  builder: (context) => DettaglioScreen(prodotto: prodotto),\n                ),\n              );\n            },\n          );\n        },\n      ),\n    );\n  }\n}\n\nclass DettaglioScreen extends StatelessWidget {\n  const DettaglioScreen({super.key, required this.prodotto});\n\n  final Prodotto prodotto;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(prodotto.nome)),\n      body: Center(\n        child: Text(\n          'Prezzo: ${prodotto.prezzo.toStringAsFixed(2)} €',\n          style: Theme.of(context).textTheme.headlineSmall,\n        ),\n      ),\n    );\n  }\n}","La Home mostra la lista dei prodotti; toccando una riga si apre il dettaglio con nome e prezzo dell'elemento selezionato.",{"id":64,"position":65,"title":66,"body":67,"code_snippet":68,"code_language":39,"expected_result":69,"demo_url":41,"video_url":41},504,5,"Ricevere un risultato dalla schermata aperta","`Navigator.push` restituisce un `Future` che si completa **quando la schermata viene chiusa**. Il valore restituito è quello passato a `Navigator.pop(context, valore)`.\n\nÈ il pattern perfetto per schermate di selezione o conferma:\n\n1. la Home fa `await Navigator.push\u003Cbool>(...)`;\n2. il dettaglio chiama `Navigator.pop(context, true)`;\n3. la Home riceve `true` e mostra una `SnackBar`.\n\n**Attenzione:** il risultato può essere `null` se l'utente torna indietro con il tasto di sistema o la freccia della AppBar. Gestisci sempre questo caso.\n\nUn altro punto importante: dopo un `await` il widget potrebbe non essere più nell'albero. Per questo si controlla `if (!context.mounted) return;` prima di usare di nuovo il `context`.","\u002F\u002F Nella HomeScreen, dentro onTap del ListTile\nonTap: () async {\n  final aggiunto = await Navigator.push\u003Cbool>(\n    context,\n    MaterialPageRoute(\n      builder: (context) => DettaglioScreen(prodotto: prodotto),\n    ),\n  );\n\n  if (!context.mounted) return;\n\n  if (aggiunto == true) {\n    ScaffoldMessenger.of(context).showSnackBar(\n      SnackBar(content: Text('${prodotto.nome} aggiunto al carrello')),\n    );\n  }\n},\n\n\u002F\u002F Nella DettaglioScreen\nbody: Center(\n  child: Column(\n    mainAxisAlignment: MainAxisAlignment.center,\n    children: [\n      Text('Prezzo: ${prodotto.prezzo.toStringAsFixed(2)} €'),\n      const SizedBox(height: 16),\n      FilledButton.icon(\n        onPressed: () => Navigator.pop(context, true),\n        icon: const Icon(Icons.add_shopping_cart),\n        label: const Text('Aggiungi al carrello'),\n      ),\n      TextButton(\n        onPressed: () => Navigator.pop(context, false),\n        child: const Text('Annulla'),\n      ),\n    ],\n  ),\n),","Toccando \"Aggiungi al carrello\" si torna alla Home e compare una SnackBar di conferma; con \"Annulla\" o con il back non compare nulla.",{"id":71,"position":72,"title":73,"body":74,"code_snippet":75,"code_language":39,"expected_result":76,"demo_url":41,"video_url":41},505,6,"Usare le rotte con nome: routes e pushNamed","Quando le schermate crescono, elencarle tutte in `MaterialApp` rende il codice più ordinato e permette di navigare senza importare ogni volta la classe della schermata.\n\nSi definisce la mappa `routes` e si usa `Navigator.pushNamed(context, '\u002Fimpostazioni')`.\n\nPer passare dati con le rotte con nome si usa `arguments`, recuperabile con `ModalRoute.of(context)!.settings.arguments`. Attenzione: qui il tipo **non** è controllato dal compilatore, quindi serve un cast (`as Prodotto`). Per questo, per i dati complessi, il costruttore visto nel passo 4 resta l'approccio più sicuro.\n\nAltri metodi utili da conoscere:\n\n- `Navigator.pushReplacementNamed` → sostituisce la rotta corrente (tipico dopo il login, per non tornare alla schermata di login);\n- `Navigator.popUntil(context, (route) => route.isFirst)` → torna alla prima schermata chiudendo tutte le altre.","class MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      initialRoute: '\u002F',\n      routes: {\n        '\u002F': (context) => const HomeScreen(),\n        '\u002Fimpostazioni': (context) => const ImpostazioniScreen(),\n      },\n    );\n  }\n}\n\n\u002F\u002F Apertura della rotta con nome\nIconButton(\n  icon: const Icon(Icons.settings),\n  onPressed: () => Navigator.pushNamed(context, '\u002Fimpostazioni'),\n),\n\nclass ImpostazioniScreen extends StatelessWidget {\n  const ImpostazioniScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Impostazioni')),\n      body: Center(\n        child: TextButton(\n          onPressed: () => Navigator.popUntil(\n            context,\n            (route) => route.isFirst,\n          ),\n          child: const Text('Torna alla Home'),\n        ),\n      ),\n    );\n  }\n}","L'icona ingranaggio nella AppBar apre la schermata Impostazioni; il pulsante \"Torna alla Home\" riporta alla prima rotta della pila.",{"id":78,"position":79,"title":80,"body":81,"code_snippet":82,"code_language":39,"expected_result":83,"demo_url":41,"video_url":41},506,7,"Errori frequenti e buone pratiche","Prima di chiudere, i problemi che incontrerai quasi sicuramente:\n\n**1. `Navigator operation requested with a context that does not include a Navigator`**\n\nSuccede quando usi il `context` del widget che contiene `MaterialApp`: in quel punto il Navigator non esiste ancora. Soluzione: estrai la home in un widget separato (come `HomeScreen`) oppure usa un `Builder`.\n\n**2. Usare il `context` dopo un `await`**\n\nDopo un'operazione asincrona il widget potrebbe essere stato smontato. Controlla sempre `context.mounted` (o `mounted` in uno `State`).\n\n**3. Dimenticare che `pop` può restituire `null`**\n\nSe l'utente esce con il back di sistema, il risultato è `null`: gestisci il caso con un valore di default o un `if`.\n\n**4. Impilare troppe schermate uguali**\n\nSe da A vai a B e da B torni ad A con un `push`, la pila cresce all'infinito. Usa `pop` per tornare indietro, oppure `pushReplacement` quando la schermata corrente non deve restare nello stack.\n\n**5. Intercettare l'uscita per chiedere conferma**\n\nUsa `PopScope` (che ha sostituito `WillPopScope` nelle versioni recenti di Flutter) per mostrare un dialog prima di abbandonare un form compilato a metà.\n\nCon questi mattoni puoi già strutturare la navigazione di app di piccola e media dimensione. Quando ti serviranno deep link, URL leggibili sul web o rotte annidate, il passo successivo naturale è `go_router`.","class FormScreen extends StatelessWidget {\n  const FormScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return PopScope(\n      canPop: false,\n      onPopInvokedWithResult: (didPop, result) async {\n        if (didPop) return;\n        final esci = await showDialog\u003Cbool>(\n          context: context,\n          builder: (context) => AlertDialog(\n            title: const Text('Uscire senza salvare?'),\n            actions: [\n              TextButton(\n                onPressed: () => Navigator.pop(context, false),\n                child: const Text('Resta'),\n              ),\n              TextButton(\n                onPressed: () => Navigator.pop(context, true),\n                child: const Text('Esci'),\n              ),\n            ],\n          ),\n        );\n        if (esci == true && context.mounted) {\n          Navigator.pop(context);\n        }\n      },\n      child: Scaffold(\n        appBar: AppBar(title: const Text('Nuovo prodotto')),\n        body: const Center(child: Text('Form...')),\n      ),\n    );\n  }\n}","Premendo il back sulla schermata del form compare un dialog di conferma; l'uscita avviene solo se l'utente conferma.",[],1789205525350]