Streams In Dart


A stream is a sequence of asynchronous events representing multiple values that will arrive in the future. Stream class deals with sequences of events instead of single events. Stream has one or more listeners, and all listeners will receive the same value. For E.g A stream is like a pipe, you put a value on the one end, and if there’s a listener on the other end that listener will receive that value.

Future vs Stream

Future Stream
Future represents the value or error that is supposed to be available in the Future. Stream is a way by which we receive a sequence of events.
A Future can provide only a single result over time. Stream can provide zero or more values or error results over time.
You can use FutureBuilder to view and interact with data. You can use StreamBuilder to view and interact with data.
It can’t listen to a variable change. But Stream can listen to a variable change.
It is like Promise in JS or Task in c#. On other hand it is like async Iterator in JS.
Syntax: Future <data_type> class_name Syntax: Stream <data_type> class_name

Types Of Stream

There are two types of streams:

  1. Single subscription streams
  2. Broadcast streams

Single Subscription Stream

By default, Streams are set up for a single subscription. They hold onto the values until someone subscribes and can only be listened to once. You will get an exception if you try to listen more than once. Any event’s value should not be missed and must be in the correct order. Inside the stream controller, there is only one stream, and only one subscriber can use that stream.

Broadcast Stream

This is the stream that is set up for multiple subscriptions. They hold onto the values until subscribers can only listen many times. You can use the broadcast stream if you want more objects to listen to the stream. It can be used for mouse events in a browser. Inside the stream controller, many streams can be used by many subscribers. E.g., You can start watching videos on such a stream at any time, and more than one subscriber can watch the video simultaneously. Similarly, you can watch again after canceling a previous subscription.


StreamController<data_type> controller = StreamController<data_type>.broadcast();

How Streams Are Created

You can create a stream in many ways. Let’s create a streamController first.

StreamController<data_type> controller = StreamController<data_type>();

Now we can access this controller through the stream property.

Stream stream =;

How To Subscribe A Stream

After getting access from the stream you subscribe to the stream by calling a listen() method.

 stream.listen((value) {
  print("Value from controller: $value");

How To Add Value To The Stream

We can add the stream by calling the add() method. Let’s add some value to the stream.


When we call the above function, we’ll get the output as:

Value from controller: 3

How To Cancel A Stream

You can cancel a stream by using the cancel() method.


How To Manage The Stream

To manage the stream, listen() method is used.

StreamSubscription<int> streamSubscription = stream.listen((value){
  print("Value from controller: $value");

Types Of Classes In Stream

Four major classes in Dart’s async libraries are used to manage streams.

Stream: It represents an asynchronous stream of data. For E.g:

 final controller = StreamController<String>();

final subscription = data) {

EventSink: It is like a stream that flows in the opposite direction.

StreamController: It simplifies stream management, automatically creating a stream and sink and also providing methods for controlling a stream’s behavior.

StreamSubscription: It saves the references of the subscription and allows them to pause, resume or cancel the flow of data they receive.

Method Used In Stream

There are four methods used in the stream: *listen(): It returns a StreamSubscription object representing the active stream-producing events. The stream subscription allows you to pause, resume the subscription after a pause, and cancel the subscription completely.


final subscription = myStream.listen()
  • onError: Stream can provide errors just like a future can; by adding an onError method, you can catch and process an mistakes.


onError: (err){

  • cancelOnError: This property or method is true by default but can be set to false to keep the subscription going even after an error.


cancelOnError : false
  • onDone: This method can execute some code when the stream is finished sending data, such as when a file has been completely read.


onDone: (){

Keywords Used In Stream

  • async*: It is mainly used in the stream that works like the async in the future.

  • yield: It is used to emit values from a generator, either async or sync. yield returns values from an Iterable or a Stream.

  • yield*: yield* is used to call its Iterable or Stream function recursively.

Example Of async

Future<int> doSomeLongTask() async {
  await Future.delayed(const Duration(seconds: 2));
  return 21;
}main() async {
  int result = await doSomeLongTask();
  print(result); // prints '42' after waiting 2 second

Example Of async*

Stream<int> countForOneMinute() async* {
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
} main() async {
  await for (int i in countForOneMinute()) {
    print(i); // prints 1 to 5, one integer per second
Show Output