Using Stream Builder in Flutter
Tags: flutter, streambuilder, async, stream
Published: 2020-02-05 11:43:00


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](https://www.youtube.com/playlist?list=PLPcmGDIpd-cWjJmCZDHJR6TqhFWP3O90p). 

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:

![Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/wff1jhrbk1ygqk57q5pr.gif)
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.

```dart
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` :

```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 ` :

```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` :

```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.

```dart
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`, and `initialData`. 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](https://api.flutter.dev/flutter/widgets/ConnectionState-class.html). 
>
> We can use `stream.hasData` ad `stream.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 :

```dart
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](https://github.com/masterashu/flutter_stream_builder_example):