Skip to main content

useStreamSubscription

Subscribes to a Stream and runs a callback for every event. Unlike useStream, which keeps only the latest value, this handles each event - so it suits event streams where nothing may be dropped.

void useErrorDialogs(BuildContext context, ErrorService service) {
useStreamSubscription(
service.errors,
(error) async => _showErrorDialog(context, error), // <- Runs for every event, awaited
strategy: StreamSubscriptionStrategy.drop, // <- A second error while the dialog is open is dropped
);
}

Future<void> _showErrorDialog(BuildContext context, AppError error) => showDialog(
context: context,
builder: (_) => AlertDialog(content: Text(error.message)),
);

Signature

void useStreamSubscription<T>(
Stream<T>? stream,
FutureOr<void> Function(T) block, {
void Function(Object, StackTrace)? onError,
void Function()? onDone,
StreamSubscriptionStrategy strategy = StreamSubscriptionStrategy.parallel,
});
  • stream - the Stream to subscribe to. A null stream is a no-op; the subscription is recreated when the stream reference changes.
  • block - called for each event. It may be async (FutureOr<void>); the strategy governs overlapping invocations.
  • onError - called on a stream error. Defaults to Zone.current.handleUncaughtError, which usually feeds the app-wide error reporter.
  • onDone - called when the stream closes.
  • strategy - how to handle an event that arrives while a previous async block is still running.

This hook returns nothing - it is a managed side effect, not a value. The callback only fires while the hook is mounted.

Strategies

StrategyBehavior
parallelHandle events concurrently - a new event starts a second block without waiting (default).
pausePause the subscription while block runs, then resume - events are buffered, none are dropped.
dropIgnore new events while a block is in flight.

Use cases

  • Event streams where every value triggers a side effect - showing a dialog per error, navigating on each command, playing a sound per notification.
  • Reacting to a stream without rendering its value (so useStream would be the wrong fit).

Caveats

  • Pick the strategy to match the side effect. The classic case for drop is a dialog that must not stack: a second event while the dialog is open is ignored. Use pause when every event matters but they must be handled one at a time.

  • block runs only while mounted, but it can still outlive a single build. If it captures values, the closure-capture caveat from useEffect applies - reach for useValueWrapper to always read the latest.

  • Errors are reported to the Zone by default rather than swallowed. Pass onError only when you have specific error UX; otherwise let the default reporting handle it.

See also