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](http://www.fluttertpoint.in/wp-content/uploads/2023/11/Orange-Modern-Minimal-Blog-Writing-YouTube-Thumbnail-1024x576.png)
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 parameterString
, indicating that it will handle events of typeString
. 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 ofStreamController<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