← Flutter & Dart

Dart avanzado

¿Qué es un `Stream` y en qué se diferencia de un `Future`?

Ver respuesta — intenta responderla en voz alta primero

Un Future entrega un solo valor en el futuro. Un Stream entrega una secuencia de valores a lo largo del tiempo (cero, uno o muchos). Piensa en el Future como una sola respuesta y en el Stream como un flujo continuo de eventos, como los datos de un sensor, mensajes de un chat o clics de usuario.

  • Future: "te aviso cuando esté listo el resultado". Una sola vez.
  • Stream: "te voy avisando cada vez que llegue algo". Muchas veces.

Para escuchar un stream tienes dos formas:

  • await for dentro de una función async: recorre los valores como si fuera un bucle.
  • .listen(): te suscribes con un callback que se ejecuta por cada valor.

Hay dos tipos de streams:

  • Single-subscription: solo un oyente, de principio a fin (archivos, peticiones HTTP).
  • Broadcast: varios oyentes al mismo tiempo (eventos de UI).

En Flutter, StreamBuilder reconstruye un widget cada vez que el stream emite un valor nuevo.

// Un stream que emite números del 1 al 3, uno por segundo
Stream<int> contar() async* {
  for (int i = 1; i <= 3; i++) {
    await Future.delayed(Duration(milliseconds: 300));
    yield i; // "yield" emite un valor al stream
  }
}

Future<void> main() async {
  // Forma 1: await for
  await for (final numero in contar()) {
    print("await for: $numero");
  }

  // Forma 2: .listen()
  contar().listen(
    (numero) => print("listen: $numero"),
    onDone: () => print("Stream terminado"),
  );
}

Salida aproximada:

await for: 1
await for: 2
await for: 3
listen: 1
listen: 2
listen: 3
Stream terminado

No cancelar la suscripción a un stream. Cuando usas .listen() obtienes un StreamSubscription; si no lo cancelas (subscription.cancel()), sigues escuchando aunque ya no lo necesites, lo que causa fugas de memoria. En Flutter, esto se cancela típicamente en el método dispose() del State.

"Un Future entrega un solo valor en el futuro; un Stream entrega una secuencia de valores a lo largo del tiempo, como eventos de un chat o lecturas de un sensor. Para consumirlo uso await for dentro de una función async, o .listen() con un callback. Los streams pueden ser de una sola suscripción o broadcast para varios oyentes. Algo importante es cancelar la suscripción cuando ya no la necesito, normalmente en dispose(), para evitar fugas de memoria. En Flutter uso StreamBuilder para reconstruir la UI cada vez que llega un dato nuevo."

Reto rápido

¿Qué palabra clave se usa para emitir un valor dentro de una función que genera un Stream (marcada con async*)?

Ver respuesta

yield. En una función async* (generador asíncrono), cada yield emite un valor al stream. Si quisieras emitir todos los valores de otro stream, usarías yield*.