Flutter: Streams and StreamBuilder

Avnish Nishad
3 min readAug 7, 2022

--

StreamBuilder is a widget that builds itself based on the latest snapshot of interaction with a stream.

This is mainly used in applications like chat application clock applications where the widget needs to rebuild itself to show the current snapshot of data.

But today we will use this to make an app for increment and decrement of the counter value. (Yeah, It boring to see the same example, but this is a perfect example to show the usage of Streams).

Let’s first understand the StreamController, streams, and sink using a simple diagram.

The StreamController provides StreamSink to add the value to the controller and Stream to get the value.

To make use of the stream controller follow these simple steps:

  1. Create a StreamController:
StreamController<int> _counterController= StreamController<int>();

2. Create getter for sink and stream for _counterController:

StreamSink<int> get counterSink => _counterController.sink;
Stream<int> get counterStream => _counterController.stream;

Example file for StreamController:

3. We have created the controller and now we want to listen to them in our widget files.

There are two ways to listen to the controller:

‣ StreamBuilder: is the widget that will listen to the StreamController and rebuild itself whenever the streamController value gets updated.

StreamBuilder<int>(stream: _counterController.counterStream,
initialData:0,
builder: (BuildContext context,AsyncSnapshot<int> snapshot,) {
if (snapshot.hasData) {
return Text(‘${_counterController.counter}’,style: Theme.of(context).textTheme.headline4,);}
else {
return Text(‘Empty data’,style:Theme.of(context).textTheme.headline4,);}
})

you can learn more about StreamBuilder here at flutter's official document
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html

‣ Using Listener:

 listener = eventStream.listen((Event event) {
switch (event) {
case Event.increment:
counter += 1;
break;
case Event.decrement:
counter -= 1;
break;
default:
}
counterSink.add(counter);
});

4 Cancel Subscription: We need to cancel the subscription of the controller otherwise when the widget which is using this get disposed then the stream subscription will remain in app memory causing memory leaks and unpredictable behavior.. We can dispose the subscription like the below inside dispose method of our stateful widget.

// dispose the listener to eliminate memory leak
dispose() {
listener?.cancel();
}
}

UI component code that uses the streamController:

5. Bad State Error:

Type of Streams:

  1. Subscription Stream: These are the default kind of stream (above we are using this stream only)and work well with single subscription but subscribing these on more than once place of code.

These kind of subscription be only fire events once a listener starts listening and stop firing events when the listener get canceled.

Question: So what will happen if we subscribe a this type of stream at multiple places?
We will get this error 😬

Bad state: Stream has already been listened to

To resolve this issue, we have second type of stream that is Broadcast stream.

2. Broadcast Stream: These streams can be listened as many time you want and will start firing events even when no one is listening to them.

A broadcast Stream can be created like below but the subscription will be same as subscription Stream.

var _counterController =StreamController<int>.broadcast();

Github Repo: https://github.com/NishadAvnish/Flutter_Blog/tree/master/stream_builder/stream_use

Also, go through my ValueListenableBuilder article which wraps a ValueListenable instead of a Stream.

Thank you, For more articles like this please follow me and don’t forget to clap, your claps motivate me to write more useful programming articles.

--

--