What is a Stream?
A Stream is a sequence of asynchronous events. It delivers a series of data events (values) and can optionally complete with a done event or an error. Unlike a Future, which delivers a single value, a stream can deliver multiple values over time. Streams are essential for handling data that arrives over time, such as user input, file I/O, network responses, or timers.
Types of Streams
Dart has two kinds of streams:
- Single‑subscription streams – allow only one listener during the stream's lifetime. They are used for sequences that are consumed once (e.g., reading a file).
- Broadcast streams – allow any number of listeners. Listeners can subscribe at any time, and they receive events that are fired after they subscribe. Broadcast streams are used for events like button clicks.
Creating a Stream
There are several ways to create a stream in Dart:
- Using an
async*function withyield.
- Using an
- Using a
StreamControllerfor manual control.
- Using a
- From an iterable with
Stream.fromIterable().
- From an iterable with
- From a Future with
Stream.fromFuture()orStream.fromFutures().
- From a Future with
- Using periodic timers with
Stream.periodic().
- Using periodic timers with
Creating a Stream with async*
Creating a Stream with StreamController
Listening to a Stream
The primary way to consume a stream is by using the listen() method. It returns a StreamSubscription which you can use to control the subscription (pause, resume, cancel).
Using await for
In an async function, you can use await for to iterate over stream events as they arrive. The loop ends when the stream closes.
Transforming Streams
Streams provide a set of methods similar to those on iterables, allowing you to transform the data:
map– applies a function to each event.
where– filters events based on a predicate.
expand– expands each event into multiple events.
take/skip– limits the number of events.
distinct– removes consecutive duplicates.
timeout– raises an error if no event arrives within a duration.
Handling Errors
Errors in a stream can be handled using onError callback in listen, or using handleError transformer. If unhandled, errors will terminate the stream.
Broadcast Streams
By default, streams are single‑subscription. To create a broadcast stream, use asBroadcastStream() on a single‑subscription stream, or create a StreamController.broadcast().
Common Patterns
- Merging streams – use
StreamGroup.mergeorStreamZip.
- Merging streams – use
- Debouncing – use
debounce(frompackage:rxdartor custom).
- Debouncing – use
- Combining latest values – use
Rx.combineLatest2(fromrxdart).
- Combining latest values – use
Key Takeaways
- A
Streamis a sequence of asynchronous events.
- A
- Use
listen()orawait forto consume stream events.
- Use
- Streams can be single‑subscription or broadcast.
- Use
StreamControllerfor manual control, orasync*functions for generator‑style streams.
- Use
- Transform streams using methods like
map,where,take, etc.
- Transform streams using methods like
- Always handle errors to avoid uncaught exceptions.