Qué es un Stream en Dart y cómo suscribirse y cancelar en initState() y dispose()
En Dart y Flutter, el concepto de Stream es fundamental para trabajar con programación reactiva y fuentes continuas de datos asíncronos. Para quienes vienen de Android con Kotlin, un Stream es el equivalente directo a un Flow de Kotlin Coroutines o a un Observable en RxJava.
Qué es un Stream y cómo funciona
Sección titulada «Qué es un Stream y cómo funciona»- Un
Futureen Dart es análogo a una llamadasuspend fun: ejecuta una tarea asíncrona, entrega un único resultado (o error) y finaliza. - Un
Streames una tubería de flujo continuo que emite múltiples datos o eventos a lo largo del tiempo.
Ejemplos habituales de uso de Streams:
- Un socket de mensajería o chat en tiempo real (cada nuevo mensaje recibido es un evento emitido por el stream).
- El sensor GPS del dispositivo móvil (emite una nueva coordenada geográfica periódicamente).
- Datos telemétricos de sensores de hardware (acelerómetro, giroscopio, podómetro).
- Actualizaciones en tiempo real procedentes de bases de datos reactivas (como Firebase Firestore o Supabase).
Gestión del ciclo de vida: Suscribirse en initState() y cancelar en dispose()
Sección titulada «Gestión del ciclo de vida: Suscribirse en initState() y cancelar en dispose()»Cuando nos suscribimos manualmente a un Stream dentro de un StatefulWidget, es imprescindible gestionar su ciclo de vida para evitar fugas de memoria (memory leaks):
class _SensorState extends State<MiSensor> { // Clase de estado mutable del sensor StreamSubscription? _suscripcion; // Referencia a la suscripción para poder cancelarla luego
@override // Evento de ciclo de vida: creación inicial del widget void initState() { // Se ejecuta una sola vez al nacer el widget super.initState(); // Llamada obligatoria al método de la clase padre _suscripcion = sensorGps.stream.listen((posicion) { // Se suscribe al flujo reactivo continuo print('Nueva posición recibida: $posicion'); // Procesa cada coordenada emitida por el sensor }); // Fin del listener } // Fin de initState
@override // Evento de ciclo de vida: destrucción final del widget void dispose() { // Se ejecuta al salir definitivamente de pantalla _suscripcion?.cancel(); // Cancela la suscripción para evitar fugas de memoria super.dispose(); // Llamada obligatoria a la liberación del padre } // Fin de dispose} // Fin de la clase _SensorState