[{"data":1,"prerenderedAt":68},["ShallowReactive",2],{"tutorial-gestire-la-navigazione-a-schede-in-flutter-con-tabbar-e-tabcontroller":3,"comments-tutorial-gestire-la-navigazione-a-schede-in-flutter-con-tabbar-e-tabcontroller":67},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"video_url":10,"difficulty":11,"estimated_minutes":12,"flutter_version":13,"status":14,"published_at":15,"meta_title":16,"meta_description":17,"category":18,"author":22,"steps":24},46,"Gestire la navigazione a schede in Flutter con TabBar e TabController","gestire-la-navigazione-a-schede-in-flutter-con-tabbar-e-tabcontroller","Impara a creare un'interfaccia a schede in Flutter usando TabBar, TabBarView e TabController, con controllo programmatico e schede scorrevoli.","La navigazione a schede è uno dei pattern di interfaccia più diffusi nelle app mobile: consente di organizzare contenuti correlati in sezioni facilmente raggiungibili con un tap o uno swipe. Flutter offre supporto nativo tramite i widget `TabBar`, `TabBarView` e la classe `TabController`.\n\nIn questo tutorial vedremo come costruire un'interfaccia a schede completa: partiremo dall'approccio rapido con `DefaultTabController`, poi passeremo al controllo manuale con `TabController` per gestire la navigazione in modo programmatico, aggiungeremo schede scorrevoli e reagiremo ai cambi di scheda. Al termine avrai una base solida e riutilizzabile per qualsiasi app.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F03db7ac0-ae43-43f2-b0b6-bbc3559378af.jpg",null,"beginner",25,"3.x","published","2026-07-23T04:30:47+00:00","Navigazione a schede in Flutter con TabBar","Guida pratica a TabBar, TabBarView e TabController in Flutter: schede scorrevoli, controllo programmatico e listener sui cambi di tab.",{"id":19,"name":20,"slug":21},1,"Guide","guide",{"id":19,"name":23},"Flutter Bot",[25,32,39,46,53,60],{"id":26,"position":19,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},310,"Creare le schede rapidamente con DefaultTabController","Il modo più veloce per iniziare è usare `DefaultTabController`, che crea e gestisce automaticamente un `TabController` per i widget discendenti. Basta indicare il numero di schede con `length` e collegare una `TabBar` (di solito nell'`AppBar`) a un `TabBarView` per i contenuti.\n\nÈ fondamentale che il numero di `Tab` nella `TabBar` corrisponda esattamente al numero di figli in `TabBarView` e al valore di `length`, altrimenti l'app genererà un'eccezione.","import 'package:flutter\u002Fmaterial.dart';\n\nclass SchedeBase extends StatelessWidget {\n  const SchedeBase({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return DefaultTabController(\n      length: 3,\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text('Le mie schede'),\n          bottom: const TabBar(\n            tabs: [\n              Tab(icon: Icon(Icons.home), text: 'Home'),\n              Tab(icon: Icon(Icons.star), text: 'Preferiti'),\n              Tab(icon: Icon(Icons.settings), text: 'Impostazioni'),\n            ],\n          ),\n        ),\n        body: const TabBarView(\n          children: [\n            Center(child: Text('Contenuto Home')),\n            Center(child: Text('Contenuto Preferiti')),\n            Center(child: Text('Contenuto Impostazioni')),\n          ],\n        ),\n      ),\n    );\n  }\n}","dart","Un'AppBar con tre schede navigabili sia con il tap sia con lo swipe orizzontale.",{"id":33,"position":34,"title":35,"body":36,"code_snippet":37,"code_language":30,"expected_result":38,"demo_url":10,"video_url":10},311,2,"Controllo manuale con TabController e SingleTickerProviderStateMixin","Quando hai bisogno di controllare le schede in modo programmatico (ad esempio cambiare tab da un pulsante) o reagire ai cambiamenti, conviene creare manualmente un `TabController`.\n\nServe uno `State` con `SingleTickerProviderStateMixin` per fornire il `vsync`, necessario alle animazioni. Ricorda sempre di liberare le risorse con `dispose()`.","class SchedeManuali extends StatefulWidget {\n  const SchedeManuali({super.key});\n\n  @override\n  State\u003CSchedeManuali> createState() => _SchedeManualiState();\n}\n\nclass _SchedeManualiState extends State\u003CSchedeManuali>\n    with SingleTickerProviderStateMixin {\n  late final TabController _tabController;\n\n  @override\n  void initState() {\n    super.initState();\n    _tabController = TabController(length: 3, vsync: this);\n  }\n\n  @override\n  void dispose() {\n    _tabController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text('Controller manuale'),\n        bottom: TabBar(\n          controller: _tabController,\n          tabs: const [\n            Tab(text: 'Uno'),\n            Tab(text: 'Due'),\n            Tab(text: 'Tre'),\n          ],\n        ),\n      ),\n      body: TabBarView(\n        controller: _tabController,\n        children: const [\n          Center(child: Text('Pagina 1')),\n          Center(child: Text('Pagina 2')),\n          Center(child: Text('Pagina 3')),\n        ],\n      ),\n    );\n  }\n}","Le schede funzionano come prima, ma ora il controller è gestito manualmente e pronto per usi avanzati.",{"id":40,"position":41,"title":42,"body":43,"code_snippet":44,"code_language":30,"expected_result":45,"demo_url":10,"video_url":10},312,3,"Cambiare scheda in modo programmatico","Avere un `TabController` esplicito ci permette di spostarci tra le schede da codice, ad esempio dopo il completamento di un'azione. Usa `animateTo(index)` per una transizione animata oppure imposta direttamente `_tabController.index`.\n\nNell'esempio aggiungiamo un `FloatingActionButton` che passa alla scheda successiva in modo ciclico.","floatingActionButton: FloatingActionButton(\n  onPressed: () {\n    final prossimo = (_tabController.index + 1) % _tabController.length;\n    _tabController.animateTo(prossimo);\n  },\n  child: const Icon(Icons.arrow_forward),\n),","Premendo il FAB si passa alla scheda successiva con un'animazione, tornando alla prima dopo l'ultima.",{"id":47,"position":48,"title":49,"body":50,"code_snippet":51,"code_language":30,"expected_result":52,"demo_url":10,"video_url":10},313,4,"Reagire ai cambi di scheda con un listener","Spesso è utile eseguire codice quando l'utente cambia scheda (analytics, ricaricare dati, aggiornare un titolo). Aggiungi un listener al controller in `initState` e ricorda di rimuoverlo nel `dispose`.\n\nUn dettaglio importante: durante lo swipe il listener viene chiamato più volte. Controlla `_tabController.indexIsChanging` per reagire solo al cambio effettivo di scheda.","@override\nvoid initState() {\n  super.initState();\n  _tabController = TabController(length: 3, vsync: this);\n  _tabController.addListener(_onTabChanged);\n}\n\nvoid _onTabChanged() {\n  if (!_tabController.indexIsChanging) {\n    debugPrint('Scheda attiva: ${_tabController.index}');\n    \u002F\u002F Qui puoi caricare dati o aggiornare lo stato\n  }\n}\n\n@override\nvoid dispose() {\n  _tabController.removeListener(_onTabChanged);\n  _tabController.dispose();\n  super.dispose();\n}","In console viene stampato l'indice della scheda ogni volta che cambia, senza chiamate duplicate durante lo swipe.",{"id":54,"position":55,"title":56,"body":57,"code_snippet":58,"code_language":30,"expected_result":59,"demo_url":10,"video_url":10},314,5,"Schede scorrevoli per molti elementi","Quando le schede sono numerose e non entrano nella larghezza dello schermo, imposta `isScrollable: true` sulla `TabBar`. In questo modo le schede potranno scorrere orizzontalmente invece di essere compresse.\n\nPuoi anche personalizzare l'indicatore, i colori del testo e l'allineamento con proprietà come `indicatorColor`, `labelColor`, `unselectedLabelColor` e `tabAlignment`.","TabBar(\n  controller: _tabController,\n  isScrollable: true,\n  tabAlignment: TabAlignment.start,\n  indicatorColor: Colors.deepPurple,\n  labelColor: Colors.deepPurple,\n  unselectedLabelColor: Colors.grey,\n  tabs: const [\n    Tab(text: 'Notizie'),\n    Tab(text: 'Sport'),\n    Tab(text: 'Tecnologia'),\n    Tab(text: 'Economia'),\n    Tab(text: 'Cultura'),\n    Tab(text: 'Viaggi'),\n  ],\n)","La TabBar mostra molte schede in fila scorrevole, con testo selezionato evidenziato in viola.",{"id":61,"position":62,"title":63,"body":64,"code_snippet":65,"code_language":30,"expected_result":66,"demo_url":10,"video_url":10},315,6,"Best practice e mantenimento dello stato delle schede","Per impostazione predefinita, il `TabBarView` mantiene in memoria i widget delle schede adiacenti, ma lo stato può andare perso se il widget viene ricostruito. Se una scheda contiene una lista con posizione di scroll o dati caricati, usa il mixin `AutomaticKeepAliveClientMixin` nel widget della scheda.\n\nAlcuni consigli finali:\n- Mantieni sempre allineati `length`, numero di `Tab` e figli di `TabBarView`.\n- Usa `DefaultTabController` per casi semplici e `TabController` manuale quando ti serve controllo o listener.\n- Chiama sempre `dispose()` sul controller creato manualmente.","class SchedaConStato extends StatefulWidget {\n  const SchedaConStato({super.key});\n\n  @override\n  State\u003CSchedaConStato> createState() => _SchedaConStatoState();\n}\n\nclass _SchedaConStatoState extends State\u003CSchedaConStato>\n    with AutomaticKeepAliveClientMixin {\n  @override\n  bool get wantKeepAlive => true;\n\n  @override\n  Widget build(BuildContext context) {\n    super.build(context); \u002F\u002F obbligatorio con il mixin\n    return ListView.builder(\n      itemCount: 50,\n      itemBuilder: (context, i) => ListTile(title: Text('Elemento $i')),\n    );\n  }\n}","Cambiando scheda e tornando indietro, la posizione di scroll e lo stato della lista vengono conservati.",[],1785219597834]