StreamProvider
StreamProvider
is similar to FutureProvider but for Streams instead of
Futures.
StreamProvider
is usually used for:
- listening to Firebase or web-sockets
- rebuilding another provider every few seconds
Since Streams naturally expose a way for listening to updates, some may think
that using StreamProvider
has a low value. In particular, you may believe that
Flutter's StreamBuilder would work just as well for listening to a Stream, but
this is a mistake.
Using StreamProvider
over StreamBuilder has numerous benefits:
- it allows other providers to listen to the stream using ref.watch.
- it ensures that loading and error cases are properly handled, thanks to AsyncValue.
- it removes the need for having to differentiate broadcast streams vs normal streams.
- it caches the latest value emitted by the stream, ensuring that if a listener is added after an event is emitted, the listener will still have immediate access to the most up-to-date event.
- it allows easily mocking the stream during tests by overriding the
StreamProvider
.
Usage example: live chat using sockets
StreamProvider
is used in when we handle stream of asynchronous data
such as Video Streaming, Weather broadcasting Apis.
final chatProvider = StreamProvider<List<String>>((ref) async* {
// Connect to an API using sockets, and decode the output
final socket = await Socket.connect('my-api', 4242);
ref.onDispose(socket.close);
var allMessages = const <String>[];
await for (final message in socket.map(utf8.decode)) {
// A new message has been received. Let's add it to the list of all messages.
allMessages = [...allMessages, message];
yield allMessages;
}
});
Then, the UI can listen to live streaming chats like so:
Widget build(BuildContext context, WidgetRef ref) {
final liveChats = ref.watch(chatProvider);
// Like FutureProvider, it is possible to handle loading/error states using AsyncValue.when
return liveChats.when(
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text(error.toString()),
data: (messages) {
// Display all the messages in a scrollable list view.
return ListView.builder(
// Show messages from bottom to top
reverse: true,
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
return Text(message);
},
);
},
);
}