[{"data":1,"prerenderedAt":22},["ShallowReactive",2],{"articolo-routing-dei-dati-con-streams-e-streambuilder-in-flutter-guida-pratica":3,"comments-article-routing-dei-dati-con-streams-e-streambuilder-in-flutter-guida-pratica":21},{"id":4,"title":5,"slug":6,"excerpt":7,"body":8,"cover_image":9,"video_url":10,"status":11,"published_at":12,"meta_title":13,"meta_description":14,"category":15,"author":19},19,"Routing dei dati con Streams e StreamBuilder in Flutter: guida pratica","routing-dei-dati-con-streams-e-streambuilder-in-flutter-guida-pratica","Scopri come gestire flussi di dati asincroni in Flutter usando Stream, StreamController e StreamBuilder per costruire UI reattive ed efficienti.","## Introduzione agli Stream in Flutter\n\nGli `Stream` sono uno dei pilastri della programmazione asincrona in Dart. Mentre un `Future` rappresenta un singolo valore disponibile in futuro, uno `Stream` rappresenta una **sequenza di eventi asincroni** nel tempo: pensa a un termometro che invia letture ogni secondo, a una connessione WebSocket o agli aggiornamenti di un database reattivo.\n\nIn questo articolo vedremo come creare, manipolare e consumare gli Stream nelle nostre interfacce Flutter in modo pulito e performante.\n\n## Single-subscription vs Broadcast\n\nDart distingue due tipi di Stream:\n\n- **Single-subscription**: può avere un solo ascoltatore. È il tipo predefinito, adatto a flussi di dati come la lettura di un file.\n- **Broadcast**: può avere più ascoltatori contemporaneamente. Utile quando più widget devono reagire allo stesso evento.\n\n```dart\n\u002F\u002F Single-subscription (default)\nfinal controller = StreamController\u003Cint>();\n\n\u002F\u002F Broadcast\nfinal broadcastController = StreamController\u003Cint>.broadcast();\n```\n\n## Creare uno Stream con StreamController\n\nLo `StreamController` è il modo più diretto per produrre eventi manualmente:\n\n```dart\nclass Contatore {\n  final _controller = StreamController\u003Cint>();\n  int _valore = 0;\n\n  Stream\u003Cint> get stream => _controller.stream;\n\n  void incrementa() {\n    _valore++;\n    _controller.sink.add(_valore); \u002F\u002F emette un nuovo evento\n  }\n\n  void errore() {\n    _controller.sink.addError('Qualcosa è andato storto');\n  }\n\n  void dispose() {\n    _controller.close(); \u002F\u002F sempre chiudere il controller!\n  }\n}\n```\n\nIn alternativa, possiamo generare Stream in modo dichiarativo con le funzioni `async*` e la parola chiave `yield`:\n\n```dart\nStream\u003Cint> contaAllaRovescia(int da) async* {\n  for (int i = da; i >= 0; i--) {\n    await Future.delayed(const Duration(seconds: 1));\n    yield i;\n  }\n}\n```\n\n## Consumare uno Stream con StreamBuilder\n\nIl widget `StreamBuilder` ricostruisce parte dell'interfaccia ogni volta che lo Stream emette un nuovo evento. È lo strumento ideale per collegare flussi di dati alla UI senza gestire manualmente i listener.\n\n```dart\nStreamBuilder\u003Cint>(\n  stream: contaAllaRovescia(10),\n  initialData: 10,\n  builder: (context, snapshot) {\n    if (snapshot.hasError) {\n      return Text('Errore: ${snapshot.error}');\n    }\n    switch (snapshot.connectionState) {\n      case ConnectionState.waiting:\n        return const CircularProgressIndicator();\n      case ConnectionState.active:\n        return Text('Mancano ${snapshot.data} secondi');\n      case ConnectionState.done:\n        return const Text('Tempo scaduto!');\n      default:\n        return const SizedBox.shrink();\n    }\n  },\n)\n```\n\n### Lo snapshot in dettaglio\n\nL'oggetto `AsyncSnapshot` fornito al builder espone:\n\n- `connectionState`: lo stato della connessione (`none`, `waiting`, `active`, `done`).\n- `hasData` \u002F `data`: presenza e valore dell'ultimo evento.\n- `hasError` \u002F `error`: presenza e dettaglio di un eventuale errore.\n\n## Trasformare gli Stream\n\nDart offre numerosi metodi per manipolare i flussi, simili a quelli delle collezioni:\n\n```dart\nfinal stream = contaAllaRovescia(10)\n    .where((n) => n.isEven)       \u002F\u002F solo numeri pari\n    .map((n) => 'Valore: $n')      \u002F\u002F trasforma in stringa\n    .take(3);                       \u002F\u002F primi 3 eventi\n```\n\nPer combinare più Stream o applicare logiche più complesse (debounce, throttle, combineLatest) si può ricorrere al pacchetto [rxdart](https:\u002F\u002Fpub.dev\u002Fpackages\u002Frxdart), che estende le API native:\n\n```dart\nimport 'package:rxdart\u002Frxdart.dart';\n\nfinal ricerca = textController.stream\n    .debounceTime(const Duration(milliseconds: 300))\n    .distinct()\n    .switchMap((query) => apiService.cerca(query));\n```\n\n## Best practice\n\nPer evitare memory leak e bug sottili, tieni a mente queste regole:\n\n- **Chiudi sempre gli StreamController** nel metodo `dispose()` del tuo widget o della classe che li gestisce.\n- **Cancella le sottoscrizioni manuali** create con `stream.listen()`, salvando lo `StreamSubscription` e chiamando `.cancel()`.\n- **Fornisci `initialData`** allo `StreamBuilder` quando possibile, per evitare uno stato di caricamento iniziale non necessario.\n- **Usa Stream broadcast** solo quando servono più ascoltatori, perché impedisce il buffering automatico degli eventi.\n- **Non creare lo Stream dentro il metodo `build`**: verrebbe ricreato a ogni ricostruzione. Inizializzalo in `initState` o in un provider.\n\n```dart\nclass _MyWidgetState extends State\u003CMyWidget> {\n  late final StreamSubscription\u003Cint> _sub;\n\n  @override\n  void initState() {\n    super.initState();\n    _sub = mioStream.listen((evento) {\n      \u002F\u002F gestione evento\n    });\n  }\n\n  @override\n  void dispose() {\n    _sub.cancel();\n    super.dispose();\n  }\n}\n```\n\n## Conclusione\n\nGli Stream e lo `StreamBuilder` rappresentano un meccanismo nativo, potente e leggero per costruire interfacce reattive in Flutter, senza dipendere necessariamente da librerie di gestione dello stato. Comprenderne il funzionamento è fondamentale anche perché molte soluzioni più evolute (come BLoC) si basano proprio su questi concetti. Padroneggiare gli Stream ti permetterà di scrivere codice asincrono più chiaro, manutenibile e privo di leak.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002Fdbc80102-b2c1-4352-bc6b-c5f72d734b89.jpg",null,"published","2026-06-23T04:00:38+00:00","Stream e StreamBuilder in Flutter: guida pratica","Impara a usare Stream, StreamController e StreamBuilder in Flutter per costruire UI reattive: esempi pratici, trasformazioni e best practice.",{"id":16,"name":17,"slug":18},1,"Guide","guide",{"id":16,"name":20},"Flutter Bot",[],1785219604969]