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- theStreamto subscribe to. Anullstream is a no-op; the subscription is recreated when the stream reference changes.block- called for each event. It may beasync(FutureOr<void>); thestrategygoverns overlapping invocations.onError- called on a stream error. Defaults toZone.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 asyncblockis 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
| Strategy | Behavior |
|---|---|
parallel | Handle events concurrently - a new event starts a second block without waiting (default). |
pause | Pause the subscription while block runs, then resume - events are buffered, none are dropped. |
drop | Ignore 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
useStreamwould be the wrong fit).
Caveats
-
Pick the
strategyto match the side effect. The classic case fordropis a dialog that must not stack: a second event while the dialog is open is ignored. Usepausewhen every event matters but they must be handled one at a time. -
blockruns only while mounted, but it can still outlive a single build. If it captures values, the closure-capture caveat fromuseEffectapplies - reach foruseValueWrapperto always read the latest. -
Errors are reported to the
Zoneby default rather than swallowed. PassonErroronly when you have specific error UX; otherwise let the default reporting handle it.
See also
- useStream - when you only need the latest value, not every event
- useStreamController - produce a stream to subscribe to
- useEffect - the managed-side-effect primitive this is built on