Using Stream Builder in Flutter
Ashutosh Chauhan
Posted on February 5, 2020
Dart & Flutter was made with asynchronicity in mind. Dart provide really good support for Futures and Streams. For those who do not have a basic idea of asynchronous programming in dart can refer to this playlist.
We will create an App which will call an API after every second and update the page whenever the response is received, which would look something like below:
Lets start with creating a new Flutter Project.
Step 1: Create the project
You can use your favorite IDE (VS Code or any other) to start a new Flutter project. Once you have create a flutter lets edit the lib/main.dart
. Keep the following code and remove everything else.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(),
);
}
}
Step 2: Requesting data from API
We will create a page which will show us the amount of likes a certain post has recieved. We won't be making the entire application only the Likes counter which will show us the amount of people who have liked a post. For simplicity we will use a dummy API which will repond with a random value.
Create a file lib/models.dart
:
class Number {
int value;
Number(this.value);
factory Number.fromJSON(Map<String, dynamic> json) {
return Number(json['value']);
}
}
We will use the class Number
which will create a class from a json object.
Next, create another file lib/requests.dart
:
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'models.dart';
Future<Number> getNumber({int num = 20}) async {
http.Response res = await http.get(Uri.http("dummyresponse.pythonanywhere.com", "/api/$num"));
return Number.fromJSON(json.decode(res.body));
}
Stream<Number> getNumbers(Duration refreshTime) async* {
while (true) {
await Future.delayed(refreshTime);
yield await getNumber();
}
}
The getNumber
is an async method which waits for a network call to a dummy response server which generates a random number. (https://dummyresponse.pythonanywhere.com/api/31
will give random no. from 0 to 31)
The getNumbers
method returns a stream which calls the API and retrieves the latest information, hence updating the information after every fixed duration.
Now, Since we have the stream let's get to the real stuff.
Step 3: Stream Builder
Create a new file lib/home_page.dart
:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:stream_builder_example/models.dart';
import 'package:stream_builder_example/requests.dart';
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder(
stream: getNumbers(Duration(seconds: 1)),
initialData: Number(0),
builder: (context, stream) {
if (stream.connectionState == ConnectionState.done) {
return Icon(
Icons.check_circle,
color: Colors.green,
size: 20,
);
}
if (stream.hasData) {
return LikeCounter(stream.data.value);
} else {
return CircularProgressIndicator();
}
},
)),
);
}
}
class LikeCounter extends StatelessWidget {
static List<Color> colors = [ Colors.green, Colors.purpleAccent, Colors.deepPurple, Colors.blueAccent, Colors.deepOrangeAccent ];
final int num;
LikeCounter(this.num);
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: 250),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: colors[num % colors.length],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.favorite, color: Colors.white),
Text(
" $num Likes",
style: TextStyle(color: Colors.white, fontSize: 20),
),
],
),
);
}
}
The LikeCounter
is a stateless widget which shows the number of likes with different colors.
Let's see how the Stream Builder works.
StreamBuilder(
stream: getNumbers(Duration(seconds: 1)),
initialData: Number(0),
builder: (context, stream) {
if (stream.connectionState == ConnectionState.done) {
return Icon(
Icons.check_circle,
color: Colors.green,
size: 20,
);
}
if (stream.hasData) {
return LikeCounter(stream.data.value);
} else {
return CircularProgressIndicator();
}
},
)
The Stream builder needs mainly 3 parameters:
stream
,builder
, andinitialData
. The initial is used as a dummy or empty element to act as the first element.The builder method receives context and snapshot of the stream.
We can use
stream.connectionState
to find the status of the stream, details of state is given here.We can use
stream.hasData
adstream.data
to build widget when the widget is build.We can also use
stream.hasError
to build when an Error occurs.
Next, change the lib/main.dart
and update HomePage()
as Material App home :
import 'home_page.dart';
// ...
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
// ...
Next Step: Use Complex Objects instead of Number
Since this is a rudimentary example we have not used a complete API. But streams provide a really good way to allow continuous chunk of data flowing through and only rebuilding the app when a value is updated. This method allows you to track values in real time without needing your user to refresh manually. This can be used in a wide variety of applications such as Social Apps, Live Sports Scores, etc.
The code for the final application can be found here:
Posted on February 5, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.