Trusted answers to developer questions

What are streams in Dart?

Free System Design Interview Course

Many candidates are rejected or down-leveled due to poor performance in their System Design Interview. Stand out in System Design Interviews and get hired in 2024 with this popular free course.

Futures play an integral part in asynchronous programming in Dart. They return single data or error events asynchronously. Streams function in a similar way, but they deal with sequences of events instead of single events. So, streams are to future objects what iterators are to integers. Streams emit data events (or elements), error events, and “done” events to notify the end of the event flow.

svg viewer

Iterating over events in a stream

We can iterate over events in a stream using an asynchronous for loop or an await for loop. The program below uses this loop to print the cumulative sum of even numbers from 11 to 1515:

import 'dart:async';
//This function yields a stream of even integers between 1 and num inclusive
Stream<int> iterateEvenStream(int num) async* {
for (int i = 1; i <= num; i++) {
if (i%2 == 0)
{
print("Yielding ${i}");
yield i;
}
}
}
//This function recieves a stream of integer events and returns their sum once the
//stream ends
//Note: The async keyword is needed for the await for loop
Future<int> evenSumStream(Stream<int> stream) async {
var evenSum = 0;
await for (var val in stream) {
evenSum += val;
print("Cumulative sum after adding ${val}: ${evenSum}\n");
}
return evenSum;
}
main() async {
var upperLimit = 15;
var stream = iterateEvenStream(upperLimit);
var finalEvenSum = await evenSumStream(stream);
print("Final sum of even numbers between 1 and ${upperLimit}: ${finalEvenSum}");
}

Subscribing to a stream

Just as with futures, actions are decided in advance when an event is emitted. If we want to call a function every time a stream emits an event, we can use Dart’s StreamSubscription object and call its listen() method. The template for this method is:

StreamSubscription<T> listen(void Function(T event) onData,
    {Function onError, void Function() onDone, bool cancelOnError});

The only required parameter for the method above is the function that’s called when the data arrives. The following example shows how listen() can be put to use:

StreamSubscription<int> subscription = stream.listen(
(data) { //this block is executed when data event is recieved by listener
print('Data: $data');
},
onError: (err) { //this block is executed when error event is recieved by listener
print('Error: ${err}');
},
cancelOnError: false, //this decides if subscription is cancelled on error or not
onDone: () { //this block is executed when done event is recieved by listener
print('Done!');
},
);

The stream in the example above is a single subscription stream. This means that the stream has been set up for an individual listener to subscribe to once throughout the stream’s lifetime. So, a repeated subscription or multiple listeners will result in an error. By default, all streams are single subscription, so they only emit events when a listener is subscribed.

If our implementation requires multiple listeners, we can use Dart’s asBroadcastStream method to create a broadcast stream on top of a single subscription stream. A broadcast stream can have multiple listeners, and if an event arrives while there is no listener, the stream tosses the event out.

To explore more features that streams have, go to Dart’s official documentation

RELATED TAGS

dart
asynchronous
futures
streams
async/await

CONTRIBUTOR

Anusheh Zohair Mustafeez
Copyright ©2024 Educative, Inc. All rights reserved
Did you find this helpful?