StreamBuilder in Flutter With Complete Examples

StreamBuilder in Flutter is a widget that allow you to listen to a stream and rebuild part of your UI whenever new data is emitted. In Flutter, the StreamBuilder widget is a powerful tool for building reactive user interfaces that respond to asynchronous data changes. It is particularly useful when working with Dart’s streams, which are a sequence of asynchronous events.

StreamBuilder in Flutter
StreamBuilder in Flutter

Here’s an overview of how StreamBuilder works and some examples to illustrate its usage.

Basic Usage of StreamBuilder in Flutter:

The StreamBuilder widget takes two main parameters:

  • stream: The stream to which it should listen.
  • builder: A callback that gets called whenever new data is available in the stream. It returns the widget tree that should be built based on the latest data.

How it works?

  • The StreamController is created with a type parameter String, indicating that it will handle events of type String.
  • controller.sink is used to get the sink, which is then used to add events to the stream.
  • The controller.stream is used to listen to events from the stream.
  • Finally, the sink is closed using sink.close().

Basic Example:

import 'dart:async';

void main() {
  // Create a StreamController with String events
  StreamController<String> controller = StreamController<String>();

  // Get the sink to add events to the stream
  Sink<String> sink = controller.sink;

  // Listen to events from the stream
  controller.stream.listen((data) {
    print('Received: $data');
  });

  // Add events using the sink
  sink.add('Event 1');
  sink.add('Event 2');

  // Close the stream when done
  sink.close();
}

How to handle errors and data with StreamBuilder in Flutter?

The below example describe to handle errors and data with showing loader before get response


import 'dart:async';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // Create a stream controller
  final StreamController<int> _controller = StreamController<int>();

  @override
  void dispose() {
    // Close the stream controller when the widget is disposed
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StreamBuilder Example'),
      ),
      body: StreamBuilder(
        stream: _controller.stream,
        builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
          if (snapshot.hasError) {
            return Text('Error: ${snapshot.error}');
          }

          if (!snapshot.hasData) {
            return Text('Waiting for data...');
          }

          return Center(
            child: Text('Data from stream: ${snapshot.data}'),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Add data to the stream
          _controller.sink.add(42);
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

Explanation:

  • _controller is an instance of StreamController<int>. It’s a controller for a stream that emits integers (int). The stream controller is used to manage the stream and handle events.
  • The StreamBuilder widget listens to the stream provided by _controller.stream. It takes a builder function that gets called whenever new data is available on the stream. The builder function receives a snapshot containing the latest asynchronous interaction with the stream.
  • If there’s an error in the stream, it displays an error message.
  • If there’s no data yet, it displays a “Waiting for data…” message.
  • If there’s data, it displays the data in the center of the screen.

In this example, the StreamBuilder listens to the _controller.stream, and whenever new data is added to the stream using _controller.sink.add(42), the builder callback is invoked, updating the UI with the latest data.

Advanced Usage of StreamBuilder Flutter:

You can use StreamBuilder with more complex streams, such as those coming from network requests or other asynchronous operations.


import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // Create a stream controller
  final StreamController<List<String>> _controller =
      StreamController<List<String>>();

  Future<List<String>> fetchData() async {
    // Simulate a network request
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos'));
    
    if (response.statusCode == 200) {
      // Parse the response and extract relevant data
      List<dynamic> data = json.decode(response.body);
      List<String> titles = data.map((item) => item['title'].toString()).toList();
      return titles;
    } else {
      throw Exception('Failed to load data');
    }
  }

  @override
  void initState() {
    super.initState();

    // Fetch data and add it to the stream
    fetchData().then((data) {
      _controller.sink.add(data);
    });
  }

  @override
  void dispose() {
    // Close the stream controller when the widget is disposed
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StreamBuilder Example'),
      ),
      body: StreamBuilder(
        stream: _controller.stream,
        builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
          if (snapshot.hasError) {
            return Text('Error: ${snapshot.error}');
          }

          if (!snapshot.hasData) {
            return Center(child: CircularProgressIndicator());
          }

          return ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (BuildContext context, int index) {
              return ListTile(
                title: Text(snapshot.data![index]),
              );
            },
          );
        },
      ),
    );
  }
}

In this example, the StreamBuilder is used to fetch data from a network request and display it in a ListView. The fetchData function returns a Future<List<String>>, and when the data is available, it is added to the stream using _controller.sink.add(data). The UI updates automatically when new data arrives.

Please note that StreamBuilder is a powerful tool in Flutter, but it’s essential to handle errors and loading states appropriately in the builder callback for a robust user experience.

Suggestions:

How to show Image in Flutter
Method Channel in Flutter Full Explanation With Examples
Flutter Bloc State Management
GetX State Management in Flutter
State Management In Flutter

Donโ€™t miss new tips!

We donโ€™t spam! Read our [link]privacy policy[/link] for more info.

Leave a Comment

Translate ยป
Scroll to Top