How to Implement the Stream Builder in Flutter

In this guide, we will learn how to implement the Stream Builder in Flutter. For these, we should know what a Stream Builder in flutter is. A widget that builds itself based on the latest snapshot of interaction with a stream. The StreamBuilder can listen to exposed streams and return widgets and catch snapshots of got stream information. We need to create a Stream and pass it as the stream argument. Then, you have to pass an AsyncWidgetBuilder which can be used to build the widget based on the snapshots of the Stream.

Main Properties:

  • builder: The build strategy currently used by this builder.
  • stream: The asynchronous computation to which this builder is currently connected, is possibly null. When changed, the current summary is updated.

Properties of Stream Builder

keyused to control how a widget is supplanted with another widget.
streamA Stream whose snapshot can be gotten to by the builder function.
initialDataThe data will be utilized to make the initial snapshot.
builderThe build procedure is utilized by this builder.

Let’s take an example of how we can implement the Stream Builder in our flutter application.

import 'package:flutter/material.dart';

class StreamScreen extends StatefulWidget {
  const StreamScreen({Key? key}) : super(key: key);

  @override
  State<StreamScreen> createState() => _StreamScreenState();
}

class _StreamScreenState extends State<StreamScreen> {
  Stream<int> generateNumber() async* {
    await Future<void>.delayed(const Duration(seconds: 2));

    for (int i = 1; i <= 10; i++) {
      await Future<void>.delayed(const Duration(seconds: 1));
      yield i;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter StreamBuilder Demo'),
        centerTitle: true,
      ),
      body: SizedBox(
        width: double.infinity,
        child: Center(
          child: StreamBuilder<int>(
            stream: generateNumber(),
            initialData: 0,
            builder: (
              BuildContext context,
              AsyncSnapshot<int> snapshot,
            ) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    const CircularProgressIndicator(),
                    Visibility(
                      visible: snapshot.hasData,
                      child: Text(
                        snapshot.data.toString(),
                        style:
                            const TextStyle(color: Colors.black, fontSize: 24),
                      ),
                    ),
                  ],
                );
              } else if (snapshot.connectionState == ConnectionState.active ||
                  snapshot.connectionState == ConnectionState.done) {
                if (snapshot.hasError) {
                  return const Text('Error');
                } else if (snapshot.hasData) {
                  return Text(snapshot.data.toString(),
                      style: const TextStyle(color: Colors.red, fontSize: 40));
                } else {
                  return const Text('Empty data');
                }
              } else {
                return Text('State: ${snapshot.connectionState}');
              }
            },
          ),
        ),
      ),
    );
  }
}

The constructor expects we pass a named contention builder whose type is AsyncWidgetBuilder. It’s a function with two parameters whose types together are BuildContext and AsyncSnapshot<T>. The subsequent boundary, which contains the current snapshot, can be utilized to figure out what ought to be rendered.

To make the function, we need to comprehend about AsyncSnapshot first. The AsyncSnapshot is an unchanging portrayal of the latest communication with an asynchronous computation. In this unique situation, it addresses the most recent communication with a Stream. We can get to the properties AsyncSnapshot to get the most recent snapshot of the Stream. One of the properties we might have to utilize is connectionState, an enum whose worth addresses the current association state to an asynchronous computation that is Steam in this unique circumstance.

AsyncSnapshot additionally has a property named hasError which can be utilized to check whether the snapshot contains a non-null error value. The hasError value will be valid if the most recent consequence of the asynchronous activity failed. For getting to the information, first, we can check whether the snapshot contains information by getting to its hasData property which will be valid if the Stream has effectively discharged any non-null value. Then, at that point, you can get the information from the data property of AsyncSnapshot.

The enum has some possible values:

  •  none: Not associated with any asynchronous computation. It can occur if the stream is null.
  •  waiting: Associated with an asynchronous computation and awaiting collaboration. In this context, it implies the Stream hasn’t been finished.
  •  active: Associated with an active asynchronous computation. For instance, if a Stream has returned any value yet is not finished at this point.
  • done: Associated with an ended asynchronous computation. In this context, it implies the Stream has finished.

Meanwhile, when we run this application in the emulator or device, we should get the UI similar to the screenshot below:

Get the full snippet from here