[{"data":1,"prerenderedAt":76},["ShallowReactive",2],{"tutorial-gestione-dello-stato-in-flutter-con-il-pattern-bloc-e-flutter-bloc":3,"comments-tutorial-gestione-dello-stato-in-flutter-con-il-pattern-bloc-e-flutter-bloc":75},{"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},17,"Gestione dello stato in Flutter con il pattern BLoC e flutter_bloc","gestione-dello-stato-in-flutter-con-il-pattern-bloc-e-flutter-bloc","Impara a separare logica e UI in Flutter usando il pattern BLoC con il pacchetto flutter_bloc: creiamo un contatore e poi una piccola app che gestisce eventi e stati in modo reattivo.","Il pattern **BLoC** (Business Logic Component) è uno degli approcci più diffusi e scalabili per gestire lo stato nelle applicazioni Flutter. L'idea di fondo è semplice: la UI invia *eventi* a un BLoC, il BLoC elabora la logica di business ed emette *stati* a cui la UI reagisce. In questo modo logica e interfaccia restano completamente separate, rendendo il codice più testabile e manutenibile.\n\nIn questo tutorial useremo il pacchetto ufficiale [`flutter_bloc`](https:\u002F\u002Fpub.dev\u002Fpackages\u002Fflutter_bloc) per costruire prima un classico contatore e poi una piccola app che carica dati in modo asincrono gestendo gli stati di caricamento, successo ed errore. Al termine avrai una base solida per applicare BLoC nei tuoi progetti reali.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002Fff92cd38-3c4a-4747-986a-f7b5fee6daf1.jpg",null,"intermediate",35,"3.x","published","2026-06-23T04:30:48+00:00","Gestione stato in Flutter con BLoC e flutter_bloc","Guida pratica al pattern BLoC in Flutter con flutter_bloc: eventi, stati, BlocBuilder e gestione di operazioni asincrone passo dopo passo.",{"id":19,"name":20,"slug":21},1,"Guide","guide",{"id":19,"name":23},"Flutter Bot",[25,32,40,47,54,61,68],{"id":26,"position":19,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},117,"Aggiungere le dipendenze","Per iniziare aggiungiamo i pacchetti `flutter_bloc` (che integra BLoC con Flutter) e `bloc` (il core indipendente dal framework). Il pacchetto `equatable` è opzionale ma molto utile: ci permette di confrontare gli stati senza scrivere manualmente l'override di `==` e `hashCode`.\n\nApri il file `pubspec.yaml` e aggiungi le dipendenze, oppure usa il comando da terminale.","flutter pub add flutter_bloc equatable","bash","Le dipendenze flutter_bloc, bloc ed equatable risultano installate nel pubspec.yaml.",{"id":33,"position":34,"title":35,"body":36,"code_snippet":37,"code_language":38,"expected_result":39,"demo_url":10,"video_url":10},118,2,"Definire gli eventi del Counter","Nel pattern BLoC la UI non modifica direttamente lo stato, ma invia **eventi**. Definiamo una classe base astratta `CounterEvent` e due eventi concreti: incremento e decremento.\n\nUsiamo `Equatable` così due eventi dello stesso tipo vengono considerati uguali, utile soprattutto nei test.","import 'package:equatable\u002Fequatable.dart';\n\nabstract class CounterEvent extends Equatable {\n  const CounterEvent();\n\n  @override\n  List\u003CObject?> get props => [];\n}\n\nclass CounterIncremented extends CounterEvent {\n  const CounterIncremented();\n}\n\nclass CounterDecremented extends CounterEvent {\n  const CounterDecremented();\n}","dart","Abbiamo un file counter_event.dart con la gerarchia degli eventi.",{"id":41,"position":42,"title":43,"body":44,"code_snippet":45,"code_language":38,"expected_result":46,"demo_url":10,"video_url":10},119,3,"Creare il Bloc che gestisce gli eventi","Ora creiamo il `CounterBloc`. Estende `Bloc\u003CCounterEvent, int>`: il primo tipo è l'evento in ingresso, il secondo è lo stato emesso (qui un semplice `int`).\n\nNel costruttore impostiamo lo stato iniziale a `0` e registriamo gli handler con `on\u003CEvento>`. Ogni handler riceve l'evento e un oggetto `emit` con cui pubblicare il nuovo stato.","import 'package:flutter_bloc\u002Fflutter_bloc.dart';\nimport 'counter_event.dart';\n\nclass CounterBloc extends Bloc\u003CCounterEvent, int> {\n  CounterBloc() : super(0) {\n    on\u003CCounterIncremented>((event, emit) => emit(state + 1));\n    on\u003CCounterDecremented>((event, emit) => emit(state - 1));\n  }\n}","Il CounterBloc reagisce agli eventi emettendo un nuovo valore intero.",{"id":48,"position":49,"title":50,"body":51,"code_snippet":52,"code_language":38,"expected_result":53,"demo_url":10,"video_url":10},120,4,"Fornire il Bloc all'albero dei widget","Per rendere il BLoC disponibile alla UI usiamo `BlocProvider`. Lo posizioniamo sopra il widget che ne ha bisogno (qui sopra `CounterPage`).\n\n`BlocProvider` si occupa anche di chiudere automaticamente il BLoC quando viene rimosso dall'albero, evitando memory leak.","import 'package:flutter\u002Fmaterial.dart';\nimport 'package:flutter_bloc\u002Fflutter_bloc.dart';\nimport 'counter_bloc.dart';\nimport 'counter_page.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: 'BLoC Demo',\n      home: BlocProvider(\n        create: (_) => CounterBloc(),\n        child: const CounterPage(),\n      ),\n    );\n  }\n}","Il CounterBloc è accessibile da tutti i widget figli di CounterPage.",{"id":55,"position":56,"title":57,"body":58,"code_snippet":59,"code_language":38,"expected_result":60,"demo_url":10,"video_url":10},121,5,"Costruire la UI con BlocBuilder e inviare eventi","Usiamo `BlocBuilder` per ricostruire solo la parte di UI che dipende dallo stato del contatore. Per inviare eventi recuperiamo il BLoC con `context.read\u003CCounterBloc>()` e chiamiamo `add`.\n\nÈ buona pratica usare `context.read` per inviare eventi (non serve ascoltare) e `BlocBuilder` o `context.watch` solo dove serve ricostruire la UI.","import 'package:flutter\u002Fmaterial.dart';\nimport 'package:flutter_bloc\u002Fflutter_bloc.dart';\nimport 'counter_bloc.dart';\nimport 'counter_event.dart';\n\nclass CounterPage extends StatelessWidget {\n  const CounterPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Contatore BLoC')),\n      body: Center(\n        child: BlocBuilder\u003CCounterBloc, int>(\n          builder: (context, count) => Text(\n            '$count',\n            style: Theme.of(context).textTheme.displayMedium,\n          ),\n        ),\n      ),\n      floatingActionButton: Column(\n        mainAxisAlignment: MainAxisAlignment.end,\n        children: [\n          FloatingActionButton(\n            heroTag: 'inc',\n            onPressed: () =>\n                context.read\u003CCounterBloc>().add(const CounterIncremented()),\n            child: const Icon(Icons.add),\n          ),\n          const SizedBox(height: 12),\n          FloatingActionButton(\n            heroTag: 'dec',\n            onPressed: () =>\n                context.read\u003CCounterBloc>().add(const CounterDecremented()),\n            child: const Icon(Icons.remove),\n          ),\n        ],\n      ),\n    );\n  }\n}","Premendo i pulsanti il numero a schermo aumenta e diminuisce in tempo reale.",{"id":62,"position":63,"title":64,"body":65,"code_snippet":66,"code_language":38,"expected_result":67,"demo_url":10,"video_url":10},122,6,"Gestire stati asincroni con stati multipli","Nei casi reali lo stato non è un semplice intero. Vediamo come modellare il caricamento di dati con stati distinti: iniziale, caricamento, successo ed errore.\n\nDefiniamo una gerarchia di stati e un BLoC che, alla ricezione di un evento `UsersRequested`, emette prima `UsersLoadInProgress` e poi `UsersLoadSuccess` o `UsersLoadFailure`. Nota l'uso di `async` nell'handler e di `emit` multipli.","import 'package:bloc\u002Fbloc.dart';\nimport 'package:equatable\u002Fequatable.dart';\n\n\u002F\u002F Eventi\nabstract class UsersEvent extends Equatable {\n  @override\n  List\u003CObject?> get props => [];\n}\n\nclass UsersRequested extends UsersEvent {}\n\n\u002F\u002F Stati\nabstract class UsersState extends Equatable {\n  @override\n  List\u003CObject?> get props => [];\n}\n\nclass UsersInitial extends UsersState {}\nclass UsersLoadInProgress extends UsersState {}\n\nclass UsersLoadSuccess extends UsersState {\n  final List\u003CString> users;\n  UsersLoadSuccess(this.users);\n  @override\n  List\u003CObject?> get props => [users];\n}\n\nclass UsersLoadFailure extends UsersState {\n  final String message;\n  UsersLoadFailure(this.message);\n  @override\n  List\u003CObject?> get props => [message];\n}\n\n\u002F\u002F Bloc\nclass UsersBloc extends Bloc\u003CUsersEvent, UsersState> {\n  UsersBloc() : super(UsersInitial()) {\n    on\u003CUsersRequested>((event, emit) async {\n      emit(UsersLoadInProgress());\n      try {\n        await Future.delayed(const Duration(seconds: 1));\n        emit(UsersLoadSuccess(['Mario', 'Giulia', 'Luca']));\n      } catch (e) {\n        emit(UsersLoadFailure('Errore nel caricamento'));\n      }\n    });\n  }\n}","Il UsersBloc emette in sequenza lo stato di caricamento e poi successo o errore.",{"id":69,"position":70,"title":71,"body":72,"code_snippet":73,"code_language":38,"expected_result":74,"demo_url":10,"video_url":10},123,7,"Reagire a tutti gli stati nella UI","Con `BlocBuilder` possiamo usare lo `switch` sui diversi tipi di stato per mostrare uno spinner, la lista o un messaggio di errore. Per effetti collaterali una tantum (snackbar, navigazione) si usa invece `BlocListener`, mentre `BlocConsumer` combina entrambi.\n\nQuesto schema rende la UI dichiarativa e perfettamente sincronizzata con lo stato del BLoC.","BlocBuilder\u003CUsersBloc, UsersState>(\n  builder: (context, state) {\n    if (state is UsersLoadInProgress) {\n      return const Center(child: CircularProgressIndicator());\n    }\n    if (state is UsersLoadSuccess) {\n      return ListView(\n        children: state.users\n            .map((u) => ListTile(title: Text(u)))\n            .toList(),\n      );\n    }\n    if (state is UsersLoadFailure) {\n      return Center(child: Text(state.message));\n    }\n    \u002F\u002F UsersInitial\n    return Center(\n      child: ElevatedButton(\n        onPressed: () =>\n            context.read\u003CUsersBloc>().add(UsersRequested()),\n        child: const Text('Carica utenti'),\n      ),\n    );\n  },\n)","La UI mostra il pulsante iniziale, poi lo spinner e infine la lista degli utenti o l'errore.",[],1785219611476]