🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Stream in Dart: Guida Completa

Codegrind TeamSep 30 2024

In Dart, gli stream sono uno strumento potente per gestire flussi di dati asincroni. Gli stream ti permettono di lavorare con sequenze di eventi che si verificano nel tempo, rendendoli particolarmente utili per gestire input utente, comunicazioni di rete e altre operazioni asincrone.

Questa guida esplorerà i concetti fondamentali degli stream in Dart, inclusi come crearli, ascoltarli e manipolarli. Imparerai anche a gestire eventi asincroni e a lavorare con i dati in tempo reale.

Cos’è uno Stream?

Uno stream è una sequenza di eventi asincroni che può essere letta e gestita in modo incrementale. Ogni volta che un nuovo evento è disponibile, viene inviato agli ascoltatori (listeners) dello stream. Gli stream possono essere utilizzati per gestire dati provenienti da diverse fonti, come l’input dell’utente o le risposte di rete.

Gli stream in Dart possono essere di due tipi:

  • Stream a singola sottoscrizione: Un stream che può essere ascoltato da un solo listener alla volta. Dopo che il listener ha completato la sottoscrizione, non è possibile ascoltare nuovamente lo stream.
  • Stream a più sottoscrizioni: Un stream che può essere ascoltato da più listener contemporaneamente. Ogni listener riceve gli eventi in modo indipendente.

Creazione di uno Stream

In Dart, puoi creare uno stream utilizzando la classe Stream e il costruttore Stream.fromIterable(), che crea uno stream a partire da una sequenza di elementi.

Esempio di Stream da una Lista

import 'dart:async';

void main() {
  // Creazione di uno stream da una lista
  var stream = Stream.fromIterable([1, 2, 3, 4, 5]);

  // Ascolto degli eventi dello stream
  stream.listen((data) {
    print('Dato ricevuto: $data');
  });
}

In questo esempio, lo stream emette gli elementi della lista uno per volta, e il listener stampa ogni elemento.

Creazione di uno Stream Personalizzato

Puoi creare uno stream personalizzato utilizzando il costruttore StreamController. Questo ti permette di gestire gli eventi dello stream in modo più flessibile.

Esempio di Stream Personalizzato

import 'dart:async';

void main() {
  // Creazione di un StreamController
  var controller = StreamController<int>();

  // Accesso allo stream
  var stream = controller.stream;

  // Ascolto degli eventi dello stream
  stream.listen((data) {
    print('Dato ricevuto: $data');
  });

  // Aggiunta di eventi allo stream
  controller.add(1);
  controller.add(2);
  controller.add(3);

  // Chiusura dello stream
  controller.close();
}

In questo esempio, creiamo uno StreamController che gestisce un stream di numeri interi. Gli eventi sono aggiunti allo stream utilizzando controller.add(), e lo stream viene chiuso con controller.close().

Gestione degli Errori negli Stream

Gli stream possono generare errori, che possono essere gestiti utilizzando il metodo onError del listener.

Esempio di Gestione degli Errori

import 'dart:async';

void main() {
  var stream = Stream<int>.fromIterable([1, 2, 3])
    ..handleError((error) {
      print('Errore: $error');
    });

  stream.listen(
    (data) {
      print('Dato ricevuto: $data');
    },
    onError: (error) {
      print('Errore: $error');
    },
  );
}

In questo esempio, handleError e onError sono utilizzati per gestire gli errori che potrebbero verificarsi durante l’emissione degli eventi.

Utilizzo degli Stream con async e await

Gli stream possono essere utilizzati con le parole chiave async e await per gestire eventi asincroni in modo più semplice.

Esempio di Utilizzo di await con Stream

import 'dart:async';

void main() async {
  var stream = Stream<int>.periodic(Duration(seconds: 1), (count) => count).take(5);

  await for (var value in stream) {
    print('Dato ricevuto: $value');
  }
}

In questo esempio, utilizziamo await for per ascoltare gli eventi dello stream e stampare i valori ricevuti. Lo stream emette un valore ogni secondo e termina dopo cinque eventi.

Operazioni Avanzate con Stream

Gli stream supportano anche diverse operazioni avanzate come la trasformazione e la combinazione degli eventi.

Esempio di Trasformazione con map

import 'dart:async';

void main() {
  var stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);

  var transformedStream = stream.map((data) => data * 2);

  transformedStream.listen((data) {
    print('Dato trasformato: $data');
  });
}

In questo esempio, utilizziamo il metodo map per trasformare ogni evento dello stream, moltiplicandolo per 2.

Esempio di Combinazione con combineLatest

import 'dart:async';

void main() {
  var stream1 = Stream<int>.periodic(Duration(seconds: 1), (count) => count).take(5);
  var stream2 = Stream<String>.periodic(Duration(seconds: 2), (count) => 'Evento $count').take(5);

  var combinedStream = Rx.combineLatest2(stream1, stream2, (int i, String s) => '$i - $s');

  combinedStream.listen((data) {
    print('Dato combinato: $data');
  });
}

In questo esempio, combineLatest2 combina due stream e crea un nuovo stream con eventi combinati.

Conclusione

Gli stream in Dart offrono un modo potente e flessibile per gestire dati asincroni e sequenze di eventi. Utilizzando StreamController, Stream.fromIterable e le operazioni avanzate come map e combineLatest, puoi gestire flussi di dati complessi in modo efficiente. Con la comprensione di questi concetti, sarai in grado di implementare soluzioni asincrone robuste e reattive nelle tue applicazioni Dart.