Introduction to Streams in Flutter

Streams are a core concept in Dart and Flutter for managing asynchronous data. They are particularly useful when dealing with data that changes over time, like user inputs, real-time updates, or event streams. This blog post will explore the types of streams in Flutter and provide an example to illustrate their use.

Types of Streams

There are two main types of streams in Flutter:

  1. Single-Subscription Streams: These streams can only be listened to once. They are perfect for cases where you have a single data sequence, such as HTTP responses or file reading operations.
  2. Broadcast Streams: These streams allow multiple listeners to subscribe simultaneously. They are ideal for scenarios where you need to emit data to multiple listeners, like real-time chat applications or live sports updates.

Single-Subscription Stream Example

A single-subscription stream allows only one listener at a time. This type of stream is ideal for operations like file reading, network requests, or any one-time data event.

Example: Fetching Data from an API

Here’s an example of a single-subscription stream that fetches data from an API:

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

void main() => runApp(MyApp());

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

class ApiDataScreen extends StatefulWidget {
@override
_ApiDataScreenState createState() => _ApiDataScreenState();
}

class _ApiDataScreenState extends State {
Stream fetchData() async* {
final response = await http.get(Uri.parse(‘https://jsonplaceholder.typicode.com/posts/1’));
if (response.statusCode == 200) {
yield json.decode(response.body)[‘title’];
} else {
throw Exception(‘Failed to load data’);
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Single-Subscription Stream’),
),
body: Center(
child: StreamBuilder(
stream: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text(‘Error: ${snapshot.error}’);
} else if (snapshot.hasData) {
return Text(‘Data: ${snapshot.data}’);
} else {
return Text(‘No data’);
}
},
),
),
);
}
}

Broadcast Stream Example

A broadcast stream allows multiple listeners to subscribe to it simultaneously. This type of stream is useful for event streams, like user inputs or real-time updates.

Example: Timer Broadcast Stream

Here’s an example of a broadcast stream that emits the current time every second and allows multiple listeners:

import ‘dart:async’;
import ‘package:flutter/material.dart’;

void main() => runApp(MyApp());

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

class TimerBroadcastScreen extends StatefulWidget {
@override
_TimerBroadcastScreenState createState() => _TimerBroadcastScreenState();
}

class _TimerBroadcastScreenState extends State {
StreamController _controller;
Stream _broadcastStream;

@override
void initState() {
super.initState();
_controller = StreamController.broadcast();
_broadcastStream = _controller.stream;

Timer.periodic(Duration(seconds: 1), (timer) {
  _controller.add(DateTime.now());
});

}

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

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Broadcast Stream’),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: StreamBuilder(
stream: _broadcastStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text(‘Error: ${snapshot.error}’));
} else if (!snapshot.hasData) {
return Center(child: Text(‘No data’));
} else {
return Center(
child: Text(
‘Current Time: ${snapshot.data}’,
style: TextStyle(fontSize: 24),
),
);
}
},
),
),
Expanded(
child: StreamBuilder(
stream: _broadcastStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text(‘Error: ${snapshot.error}’));
} else if (!snapshot.hasData) {
return Center(child: Text(‘No data’));
} else {
return Center(
child: Text(
‘Another Listener: ${snapshot.data}’,
style: TextStyle(fontSize: 24),
),
);
}
},
),
),
],
),
);
}
}

Explanation

  1. Single-Subscription Stream:
    • The fetchData method uses async* to create a single-subscription stream that fetches data from an API.
    • Only one StreamBuilder can listen to this stream at a time.
    • This is suitable for one-time data fetching operations.
  2. Broadcast Stream:
    • The _controller is a StreamController with .broadcast(), allowing multiple listeners.
    • A Timer.periodic adds the current time to the stream every second.
    • Multiple StreamBuilder widgets can listen to this stream simultaneously.
    • This is useful for events that need to be broadcasted to multiple listeners.

Conclusion

Understanding the difference between single-subscription and broadcast streams helps you choose the right stream type for your Flutter application. Use single-subscription streams for one-time events and broadcast streams for continuous data streams that need multiple listeners. By leveraging streams effectively, you can build responsive and dynamic applications in Flutter.

Leave A Comment

Recommended Posts