useStream
Subscribes to a Stream and exposes its latest value as an AsyncSnapshot. It is the hook equivalent of the StreamBuilder widget.
Widget buildPosition(BuildContext context, Stream<String> position) {
final snapshot = useStream(position); // <- Subscribes, rebuilds on each event
if (snapshot.hasError) return const Text('Location unavailable');
return Text(snapshot.data ?? 'Locating...'); // <- Latest value, or null before the first event
}
Signature
AsyncSnapshot<T> useStream<T>(Stream<T>? stream, {T? initialData, bool preserveState = true});
T? useStreamData<T>(
Stream<T>? stream, {
T? initialData,
bool preserveState = true,
void Function(Object error, StackTrace stackTrace)? onError,
});
stream- theStreamto listen to. Anullstream leaves the snapshot in its initial state.initialData(nullby default) - the value to expose before the first event.preserveState(trueby default) - when thestreaminstance changes, keep the last value during the gap before the new subscription emits. Set it tofalseto reset toinitialData.
useStreamData returns data directly and pipes the snapshot through useAsyncSnapshotErrorHandler, so a stream error is reported rather than silently swallowed; onError chooses where it goes.
String? useCurrentPosition(LocationService service) {
final stream = useMemoized(() => service.position);
return useStreamData(stream); // <- Returns the latest value directly; reports errors itself
}
Use cases
- Reflecting a continuously updating value in the UI - GPS location, sensor readings, a clock.
- Exposing a reactive backend value, such as the current authenticated user or a live document.
Caveats
-
useStreamexposes only the latest value. If the stream emits several values between two builds, the intermediate ones are lost. For an event stream where every value must be handled, useuseStreamSubscriptioninstead. -
A new
streaminstance cancels the old subscription and starts a new one. Building theStreaminline subscribes afresh on every build, so pair it withuseMemoized- or useuseMemoizedStream, which packages that for you. -
A stream error lands in
snapshot.errorand stays there silently. Handle it viauseAsyncSnapshotErrorHandler, or useuseStreamData, which wires that handler for you. Unlike aFuture, a stream stays subscribed after an error, so it may keep emitting afterwards.
See also
- useMemoizedStream -
useStreampaired withuseMemoizedin a single call - useStreamSubscription - run a side effect for every event instead of reading the latest value
- useFuture - the same pattern for a one-shot
Future - useAsyncSnapshotErrorHandler - surface errors carried in an
AsyncSnapshot