Streams
Prerequisites: The event loop
A Future delivers one value later. A Stream delivers many
values over time — user input events, messages from a server, lines
from a file, location updates.
Creating a stream with async*
An async* function returns a stream and produces values with yield:
Stream<int> countdown(int from) async* {
for (var i = from; i >= 1; i--) {
await Future.delayed(const Duration(milliseconds: 100));
yield i;
}
}
Future<void> main() async {
await for (final n in countdown(3)) {
print(n);
}
final doubled = await countdown(4).map((n) => n * 2).where((n) => n > 2).toList();
print(doubled);
}
3
2
1
[8, 6, 4]
await forprocesses each value as it arrives, finishing when the stream ends.- Streams have the familiar
map,where,take,skip, and more.toList()waits for the stream to finish and collects everything.
Listening
listen subscribes with callbacks instead of a loop, and returns a
subscription you can cancel:
Stream<int> ticks() async* {
var i = 0;
while (true) {
await Future.delayed(const Duration(milliseconds: 100));
yield i++;
}
}
Future<void> main() async {
final subscription = ticks().listen(
(tick) => print('tick $tick'),
onError: (Object e) => print('error: $e'),
onDone: () => print('done'),
);
await Future.delayed(const Duration(milliseconds: 350));
await subscription.cancel();
print('cancelled');
}
tick 0
tick 1
tick 2
cancelled
Always cancel subscriptions you no longer need — for example, in a
Flutter widget's dispose method. A forgotten subscription keeps
running and holding memory.
Errors in streams
Stream<int> readings() async* {
yield 21;
yield 22;
throw StateError('sensor disconnected');
}
Future<void> main() async {
try {
await for (final value in readings()) {
print('reading: $value');
}
} on StateError catch (e) {
print('stopped: ${e.message}');
}
}
reading: 21
reading: 22
stopped: sensor disconnected
Single-subscription vs broadcast
Most streams allow only one listener. A broadcast stream allows many — the next lesson shows how to create both.
Common mistakes
- Listening to a single-subscription stream twice:
Bad state: Stream has already been listened to. - Forgetting to cancel subscriptions in long-lived apps.