Skip to main content

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 - the Stream to listen to. A null stream leaves the snapshot in its initial state.
  • initialData (null by default) - the value to expose before the first event.
  • preserveState (true by default) - when the stream instance changes, keep the last value during the gap before the new subscription emits. Set it to false to reset to initialData.

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

  • useStream exposes 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, use useStreamSubscription instead.

  • A new stream instance cancels the old subscription and starts a new one. Building the Stream inline subscribes afresh on every build, so pair it with useMemoized - or use useMemoizedStream, which packages that for you.

  • A stream error lands in snapshot.error and stays there silently. Handle it via useAsyncSnapshotErrorHandler, or use useStreamData, which wires that handler for you. Unlike a Future, a stream stays subscribed after an error, so it may keep emitting afterwards.

See also