StreamTransformer în Dart

Transformă un Stream<S> într-un Stream<T>; fromHandlers îl construiește din funcții pentru date, erori și final.

Sintaxă
import 'dart:async';

abstract interface class StreamTransformer<S, T>
factory StreamTransformer.fromHandlers({void handleData(S data, EventSink<T> sink)?, void handleError(Object error, StackTrace stackTrace, EventSink<T> sink)?, void handleDone(EventSink<T> sink)?})
Stream<T> bind(Stream<S> stream)

Ce face StreamTransformer

Un StreamTransformer<S, T> primește un stream și dă alt stream, prin metoda bind; de obicei îl folosești prin stream.transform(t). Spre deosebire de map, poate emite mai multe valori pentru una, niciuna, sau poate păstra o stare între valori.

StreamTransformer.fromHandlers este cel mai simplu mod de a crea unul: handleData primește fiecare valoare și un sink în care scrii rezultatele, handleError primește erorile, iar handleDone rulează la final (poți scrie ultimele valori înainte de sink.close()). Funcțiile care lipsesc trimit evenimentele mai departe neschimbate.

Parametri

Parametru Descriere
handleData Ce faci cu fiecare valoare; rezultatele se scriu în sink.
handleError Opțional: ce faci cu fiecare eroare.
handleDone Opțional: ce faci la final; trebuie să chemi sink.close().

Ce returnează

Un transformator care se aplică cu stream.transform(...).

Exemple

Exemplul 1: Grupare câte 2

Transformatorul păstrează o stare (lista curentă) și la final trimite ce a rămas.

import 'dart:async';

StreamTransformer<String, List<String>> grupe(int marime) {
  var curent = <String>[];
  return StreamTransformer.fromHandlers(
    handleData: (x, sink) {
      curent.add(x);
      if (curent.length == marime) {
        sink.add(curent);
        curent = [];
      }
    },
    handleDone: (sink) {
      if (curent.isNotEmpty) sink.add(curent);
      sink.close();
    },
  );
}

Future<void> main() async {
  final elevi = Stream.fromIterable(['Ana', 'Mihai', 'Ioana', 'Radu', 'Elena']);
  await elevi.transform(grupe(2)).forEach((g) => print('Bancă: $g'));
}

Rezultat

Bancă: [Ana, Mihai]
Bancă: [Ioana, Radu]
Bancă: [Elena]

Exemplul 2: Erorile transformate în valori

import 'dart:async';

final sigur = StreamTransformer<int, String>.fromHandlers(
  handleData: (n, sink) => sink.add('ok: $n'),
  handleError: (e, st, sink) => sink.add('eroare înlocuită: $e'),
);

Future<void> main() async {
  final s = Stream<int>.multi((c) {
    c.add(1);
    c.addError(StateError('citire eșuată'));
    c.add(3);
    c.close();
  });
  print(await s.transform(sigur).toList());
}

Rezultat

[ok: 1, eroare înlocuită: Bad state: citire eșuată, ok: 3]

Greșeli frecvente

  • Scrii handleDone și uiți sink.close(). Stream-ul rezultat nu se mai închide.
  • Ții starea într-o variabilă globală. Dacă folosești același transformator pe două stream-uri, starea se amestecă; creează-l printr-o funcție, ca în exemplu.

Comenzi înrudite

  • Stream.transform: Trece stream-ul printr-un StreamTransformer, de exemplu utf8.decoder sau LineSplitter, și dă stream-ul rezultat.
  • StreamTransformer.fromBind: Creează un transformator dintr-o funcție care primește tot stream-ul și returnează stream-ul nou.
  • StreamSink: Interfața „capătului de scriere” al unui stream: add, addError, addStream, close și done.
  • LineSplitter: Împarte un text în rânduri, recunoscând \n, \r\n și \r; funcționează și pe fluxuri cu transform.