Módulo 03: Dart Deep Dive

Streams

Asincronia

# Streams en Dart Un **Stream** es una secuencia de eventos asíncronos. Es como una tubería (pipe) por donde fluyen datos a lo largo del tiempo. Si un `Future` representa un valor que estará listo en el futuro (una caja que llegará por correo), un `Stream` representa una serie de valores (una cinta transportadora). ## Tipos de Streams ### 1. Single Subscription (Suscripción Única) - Solo permite un oyente (listener). - Si intentas escuchar dos veces, lanza error. - Común para: Lectura de archivos, peticiones HTTP (respuesta por chunks). ### 2. Broadcast (Difusión) - Permite múltiples oyentes. - Común para: Eventos de UI, cambios de estado, WebSockets. ## Creando Streams ### Stream.fromIterable ```dart Stream countStream(int max) async* { for (int i = 1; i <= max; i++) { yield i; // Emite un valor await Future.delayed(Duration(seconds: 1)); } } void main() async { // Usando await for (bucle asíncrono) await for (final value in countStream(3)) { print(value); } } ``` ### StreamController Para control manual total. ```dart import 'dart:async'; void main() { final controller = StreamController(); // Escuchar controller.stream.listen( (data) => print('Recibido: $data'), onError: (err) => print('Error: $err'), onDone: () => print('Stream cerrado'), ); // Emitir eventos controller.add('Hola'); controller.add('Mundo'); controller.addError('Algo salió mal'); // Cerrar controller.close(); } ``` ## Transformando Streams Los Streams tienen métodos poderosos similares a las Listas (`map`, `where`, etc.). ```dart Stream numbers = Stream.fromIterable([1, 2, 3, 4, 5]); void main() { numbers .where((n) => n % 2 == 0) // Filtrar pares: 2, 4 .map((n) => n * 10) // Multiplicar: 20, 40 .listen(print); } ``` ## StreamBuilder en Flutter Es el widget fundamental para consumir Streams en la UI. ```dart Stream _counterStream() async* { while (true) { await Future.delayed(Duration(seconds: 1)); yield DateTime.now().second; } } class ClockWidget extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( stream: _counterStream(), builder: (context, snapshot) { if (snapshot.hasError) return Text('Error'); if (!snapshot.hasData) return CircularProgressIndicator(); return Text('Segundos: ${snapshot.data}'); }, ); } } ``` ## Manejo de Errores ```dart stream.listen( (data) { ... }, onError: (error) { print('Manejando error: $error'); }, cancelOnError: false, // Seguir escuchando después de un error ); ``` ## RxDart (Reactive Extensions) Aunque Dart tiene streams potentes, el paquete `rxdart` añade operadores avanzados muy útiles para arquitecturas como BLoC. - `debounceTime`: Esperar a que el usuario deje de escribir. - `combineLatest`: Combinar varios streams (ej. email y password válidos). - `switchMap`: Cancelar peticiones anteriores si llega una nueva. ## Conclusión - Usa **Streams** para datos que llegan en múltiples momentos (eventos, sockets, timers). - Usa **StreamController** para crear tus propios streams. - Usa **StreamBuilder** para actualizar la UI reactivamente. - Recuerda siempre cancelar las suscripciones (`subscription.cancel()`) si no usas StreamBuilder, para evitar fugas de memoria.

Este apartado profundiza en los conceptos clave, proporcionando ejemplos prácticos y mejores prácticas para su aplicación en proyectos reales.

Al comprender estos detalles, podrás diseñar soluciones más robustas, mantenibles y escalables en tus aplicaciones Flutter y Dart.