Before I dive into StreamBuilder let me quickly digress to Stream, StreamController, they are the building blocks of Streambuilder.
What is a Stream
Well, you are thinking, Is that stream I'm talking about! No it's not But think of it has the typical stream, A stream is an Event that's uncontrolled, that can be generated from a Future, network access, reading of files(local/network) e.t.c.which has an input(i.e where it's coming from) and output(i.e where it's going). You can also think of Stream as a pipe that has an input called the sink and output called stream. How to create a stream
- From Future: Stream.fromFuture(future); -From Iterable: Stream.fromIterable(Iterable <T> elements); -From StreamController
Read more about Stream:api.flutter.dev/flutter/dart-async/Stream-c..
StreamController
the GIF image above explains a lot about StreamController, a StreamController allows you to have control over your Stream, i.e you can listen to a stream, add Stream to your StreamController, you can pause, stop your stream. if there are errors, StreamController allows you to catch those errors and handle them gracefully, StreamController gives you access to the 360-degree life cycle of a stream. StreamController API exposes some method such as: sink(which allows you to add data event) stream(the stream StreamController is controlling) map, transform (which allows you to modify or intercept your data before it emits out as a stream).
Adding Event via StreamController StreamController exposes a sink method which allows you to add item to the stream which will be emitted as a stream using stream method.
StreamController _streamController = StreamController();
//Add event/Data to the StreamController
_streamController.sink.add(data); //NB: data can be any type i.e primitive/non-primitive
//data added to the Controller via the sink is emitted as Stream via stream method.
Stream strem = _streamController.stream;
Read more about stream api.flutter.dev/flutter/dart-async/StreamCo...
StreamBuilder
StreamBuilder is a widget that allows you to convert your stream object to a widget. rebuilding widget is basically done using setState, but performance-wise it isn't efficient because setState rebuild the whole widget and every child(s) widget, come to think of it, if you only need to rebuild a single widget then setState is a bad idea, so using StreamBuilding is your best shot. You can rebuild a single widget of your choice without touching or rebuilding the whole widget family tree.
//let assume we have a stream will want to consume
Stream stream =Stream.fromFuture(Favourite().getAllFavourite(data));
StreamBuilder(
stream: stream,
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.data !=null){
return Widget();
}
},
);
NB: in the above snippet we try to convert our Stream to a widget, in the builder function which accepts BuildContext and AsyncSnapshot as a parameter it looks exactly like FutureBuilder if you are familiar with it. we can get access to our Stream data using snapshot.data.
if you want to check your connection state, maybe to perform a specific operation, you can do that with the below snippet;
StreamBuilder(
stream: stream,
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.connectionState == ConnectionState.active){
// Your statement when the connection mode is in active state
}
else if(snapshot.connectionState == ConnectionState.done){
//Your statement when the operation has finished execution
}
else if (snapshot.connectionState == ConnectionState.none){
//Your statement when the connection is not available
}
else if( snapshot.connectionState == ConnectionState.waiting){
//Your statement when the connection state is in waiting mode
}
else{
// else Statement
}
},
);
below is the demo of how I used Streambuilder on flutter boilerplate counter app instead of setState.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
StreamController _streamController ;
Stream _stream;
@override
void initState() {
_streamController = StreamController();
_stream= _streamController.stream;
}
void _incrementCounter() {
_streamController.sink.add(_counter++);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
StreamBuilder(
stream: _stream,
builder: (BuildContext context, AsyncSnapshot snapshot){
return Text(
snapshot.data!=null ? snapshot.data.toString() : "0",
style: Theme.of(context).textTheme.display1,
);
}
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
@override
void dispose() {
}
}
you can check out the source code on Github: github.com/DAMMAK/flutter-counter-app-with-..