DEV Community

MD Sarfaraj for This is Learning

Posted on

Handling Asynchronous Data In Flutter - The Power Of Streams

Flutter is a popular open-source mobile app development framework that allows developers to create high-performance, visually appealing, and feature-rich mobile apps. The stream concept is one of the essential features of Flutter, which will enable you to handle asynchronous data. This article will explore Flutter's stream, its uses, and how it works.

What is a Stream?

A flutter stream is a sequence of asynchronous data that can be processed and consumed efficiently. In a stream, continuous data is delivered to an application in a continuous flow. This can handle input events, network responses, and even animations.

Streams are intended to handle asynchronous data that may take an unknown amount of time to arrive, allowing the developer to handle the data as soon as it becomes available. Streams are also very efficient because they only process available data and don't waste resources waiting for data.

Suppose you have a weather app that displays a list of weather updates. Instead of constantly checking for new updates, you can use a stream to listen for and display updates from a server in real-time. When a new update is received, it is sent through the stream, and your app can respond accordingly.

Types of Streams

There are two types of streams in Flutter,

Single Subscription Stream

A stream that can only be listened to once. The stream is closed when the data has been consumed.

Broadcast Stream

A stream that can be listened to repeatedly. Multiple subscribers can listen to the same stream at the same time.

How to work with Streams

To work with streams in Flutter, follow the steps below for effective stream integration in your application.

STEP 1

Import the dart:async library.

import 'dart:async';
Enter fullscreen mode Exit fullscreen mode

The dart:async library is used in Dart and Flutter to work with asynchronous programming.

STEP 2

Create a StreamController object.

final StreamController<int> _controller = StreamController<int>();
Enter fullscreen mode Exit fullscreen mode

In Flutter, the StreamController object allows you to create, add data, and listen to streams. In this case, we're making a new StreamController to handle integers.

STEP 3

Add data to the stream.

_controller.sink.add(1);
_controller.sink.add(2);
_controller.sink.add(3);
Enter fullscreen mode Exit fullscreen mode

The StreamController sink property is used to add data to the stream. In this case, we're adding three integer values to the stream: 1, 2, and 3.

STEP 4

Create a StreamSubscription object:

final StreamSubscription<int> _subscription = _controller.stream.listen((event) {
  print(event);
});
Enter fullscreen mode Exit fullscreen mode

The object _subscription is created to listen to the data stream. The StreamController's listen method registers a callback function that will be called whenever a new event is added to the stream. In this case, we're using the print function to print the value of each new event to the console.

STEP 5

Close the StreamSubscription and StreamController objects

_subscription.onDone(() {
  _controller.close();
});
Enter fullscreen mode Exit fullscreen mode

The StreamSubscription's onDone method registers a callback function that will be called when the subscription has finished listening to the stream. In this case, we're calling the StreamController's close method to close the stream and release any associated resources.

Let's create a counter app with a stream,

We will now develop a basic counter application using streams, incorporating the concepts we have learned thus far.

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

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

class MyApp extends StatelessWidget {
 const MyApp({super.key});

 // This widget is the root of your application.
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'Flutter Demo',
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: const MyHomePage(title: 'Flutter Demo Home Page'),
   );
 }
}

class MyHomePage extends StatefulWidget {
 const MyHomePage({super.key, required this.title});

 final String title;

 @override
 State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 final StreamController<int> _streamController = StreamController<int>();

 Stream<int> get counterStream => _streamController.stream;

 int _counter = 0;

 @override
 void dispose() {
   _streamController.close();
   super.dispose();
 }

 void _incrementCounter() {
   _counter++;
   _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>[
             const Text(
               'You have pushed the button this many times:',
             ),
             StreamBuilder<int>(
               stream: counterStream,
               initialData: _counter,
               builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                 return Text(
                   '${snapshot.data}',
                   style: Theme.of(context).textTheme.headlineMedium,
                 );
               },
             ),
           ]),
     ),
     floatingActionButton: FloatingActionButton(
       onPressed: _incrementCounter,
       tooltip: 'Increment',
       child: const Icon(Icons.add),
     ), // This trailing comma makes auto-formatting nicer for build methods.
   );
 }
}
Enter fullscreen mode Exit fullscreen mode

We used a StreamBuilder widget to create a user interface that updates based on a data stream. A stream is a sequence of asynchronous events that can be observed, and the StreamBuilder widget listens to this stream and updates the user interface whenever a new event is received.

To use a StreamBuilder widget, you provide it with a stream and a builder function. The builder function takes two arguments: a BuildContext for building the user interface and an AsyncSnapshot containing the most recent data emitted by the stream.

Use cases of Stream

In Flutter, streams are versatile tools that can be used in many ways. Here are some examples,

Network Requests

A stream can receive data from a server in real-time and process it.

Animation

Streams can update the animation's state as it plays.

User Input

Streams can listen to events in text fields and buttons and update the app's state accordingly.

State Management

Streams can be used to manage the app state. You can create a stream that emits events when the state changes and use it to update the UI.

Conclusion

Asynchronous data handling is one of the most important aspects of Flutter development. They are efficient, user-friendly, and can be used in various contexts, such as network requests, animations, and user input. A solid understanding of streams is essential for any Flutter developer, and we hope this article has provided you with an excellent introduction to this vital concept.

Top comments (2)

Collapse
 
krlz profile image
krlz

Flutter has gain big traction in the last years, I didn't know it could work with stream data thanks for sharing

Collapse
 
yourmdsarfaraj profile image
MD Sarfaraj

Thank you for your comment! Indeed, Flutter has gained a lot of popularity in recent years as a powerful and flexible framework for building mobile and web applications. And one of the great features of Flutter is its ability to work with stream data, making it easier to handle and process real-time data in your application. If you have any other questions feel free to ask!