Beginner · Async Dart and packages · topic 10 of 50 · 30 min
Streams
Explanation
Many values over time
Some data does not arrive once; it keeps coming: the score of a cricket match, a delivery rider's location, new chat messages, the user typing in a search box. A Stream is a sequence of asynchronous events. It can deliver values, errors, and finally a "done" signal.
Streams are everywhere in apps even when you do not create them yourself: a text field's changes, a sensor's readings, a database query that updates when rows change, or messages from a WebSocket. Learning to read and write stream code makes all of these feel the same.
Like a Future, a Stream can fail. An error event does not necessarily end the stream; it depends on the source. Always provide error handling with onError in listen, or a try/catch around await for, so one bad event does not crash the screen.
Creating a stream with async*
Stream<int> countdown(int from) async* {
for (var i = from; i >= 1; i--) {
yield i; // send a value
await Future.delayed(const Duration(seconds: 1));
}
}
Future<void> main() async {
await for (final s in countdown(3)) {
print('OTP expires in $s s');
}
print('OTP expired');
}listen and subscriptions
final sub = countdown(5).listen(
(value) => print('tick $value'),
onError: (e) => print('error: $e'),
onDone: () => print('done'),
);
// later, for example when the screen closes:
// sub.cancel();StreamController
A StreamController lets you push values into a stream from ordinary code, for example when a WebSocket message arrives. By default its stream is single-subscription: only one listener, and values are buffered until someone listens. A broadcast stream allows many listeners but does not buffer for late ones.
import 'dart:async';
class ScoreBoard {
final _controller = StreamController<String>.broadcast();
Stream<String> get scores => _controller.stream;
void update(String score) => _controller.add(score);
void dispose() => _controller.close(); // always close what you create
}
void main() {
final board = ScoreBoard();
board.scores.listen((s) => print('App: $s'));
board.scores.listen((s) => print('Widget: $s'));
board.update('IND 245/3 (40)');
board.dispose();
}Transforming streams
final evensDoubled = Stream.fromIterable([1, 2, 3, 4])
.where((n) => n.isEven)
.map((n) => n * 2);
evensDoubled.listen(print); // 4, then 8How Flutter uses streams
Flutter's StreamBuilder widget listens to a stream and rebuilds part of the screen with the latest value. Cloud Firestore's snapshots() returns a stream, so a chat screen updates the moment a new message is written. State management libraries such as Bloc are also built on streams (module 7).
Common mistakes
- Forgetting to cancel subscriptions in a widget's dispose: memory leaks and work on screens that are gone.
- Not closing StreamControllers you create.
- Listening twice to a single-subscription stream: "Bad state: Stream has already been listened to". Use a broadcast stream or a single listener.
- Using a Stream when only one value is expected. Use a Future.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Streams · 1/6
A Future gives one value. A Stream gives many values over time.
Prefer a video by a teacher? Find free videos on “dart streams async* yield streamcontroller broadcast tutorial” on YouTube ↗
In real life: Live cricket score widget
A sports news app shows a small live score card on its home screen. The score arrives over the network as a stream of updates. A StreamBuilder shows "Loading..." until the first value, then the latest score, and the subscription is cancelled automatically when the card leaves the screen.
Stream<String> liveScore() async* {
const updates = ['IND 0/0', 'IND 45/1 (8)', 'IND 112/2 (18)'];
for (final u in updates) {
await Future.delayed(const Duration(seconds: 2));
yield u;
}
}
// In a Flutter widget (module 4 onward):
// StreamBuilder<String>(
// stream: liveScore(),
// builder: (context, snap) => Text(snap.data ?? 'Loading...'),
// )Practice (0/3 done)
What does this print? Stream<int> nums() async* { yield 1; yield 2; yield 3; } Future<void> main() async { var total = 0; await for (final n in nums()) total += n; print(total); }
You get "Bad state: Stream has already been listened to". Why?
Write a class OrderTracker with a private broadcast StreamController<String>, a public stream getter, a method update(status) and dispose(). In main, add two listeners, push "Placed", "Cooking" and "Out for delivery", then dispose. Also write an async* function that yields the numbers 10 to 1 with a 100 ms delay and sum them with await for.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Isolates for heavy workAsync Dart and packages · same module
- Packages and pubAsync Dart and packages · same module
- Interview questions: DartFlutter careers and interviews
- Streams: filter, map and collectJava Programming (Core to Spring Boot) · Lambdas, streams and files
- async and awaitJavaScript Programming (language in depth) · Asynchronous JavaScript
- Firebase in FlutterNetworking, storage and Firebase
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Which keyword emits a value from an async* function?
Answer: yield
yield sends the next value.
What does listen() return?
Answer: A StreamSubscription you can cancel
Keep it to cancel later.
Which stream type supports several listeners?
Answer: Broadcast
Broadcast streams allow multiple listeners.
Which Flutter widget rebuilds with the latest value of a stream?
Answer: StreamBuilder
StreamBuilder listens and rebuilds.
Where does an error from an awaited Future appear?Related
Answer: At the await, where try/catch can handle it
It is rethrown at the await.
Which keyword creates a compile-time constant?Related
What happens if you call two terminal operations on the same stream?Related
async function f() { return 3; } What is f()?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Async Dart and packages” before its exam: Isolates for heavy work, Packages and pub.
- Module 3 exam: Async Dart and packages · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Flutter setup and your first app. 50 topics left in the course before the final exam.