Stream in Dart: Guida Completa
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.