Zum Hauptinhalt springen

StreamProvider

caution

The content of this page may be outdated.
It will be updated in the future, but for now you may want to refer to the content in the top of the sidebar instead (introduction/essentials/case-studies/...)

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 or Live chat as follows:



Stream<List<String>> chat(Ref 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 switch (liveChats) {
// Display all the messages in a scrollable list view.
AsyncData(:final value) => ListView.builder(
// Show messages from bottom to top
reverse: true,
itemCount: value.length,
itemBuilder: (context, index) {
final message = value[index];
return Text(message);
},
),
AsyncError(:final error) => Text(error.toString()),
_ => const CircularProgressIndicator(),
};
}