Skip to main content

useMemoizedStream

Combines useMemoized and useStream: it memoizes the Stream returned by a factory and subscribes to it, re-subscribing only when keys change.

Widget buildOrders(BuildContext context, OrderService service, String userId) {
// Re-subscribes only when userId changes.
final snapshot = useMemoizedStream(
() => service.streamOrders(userId),
keys: [userId],
);

if (snapshot.hasError) return const Text('Could not load orders');
final orders = snapshot.data;
if (orders == null) return const Text('Loading...'); // <- null until the first event
return Text('${orders.length} orders');
}

Signature

AsyncSnapshot<T> useMemoizedStream<T>(
Stream<T>? Function() block, {
T? initialData,
bool preserveState = true,
HookKeys keys = hookKeysEmpty,
});

T? useMemoizedStreamData<T>(
Stream<T>? Function() block, {
T? initialData,
bool preserveState = true,
void Function(Object, StackTrace)? onError,
HookKeys keys = hookKeysEmpty,
});
  • block - factory that produces the Stream. It runs on first build and again whenever keys change. Returning null means "no subscription yet" - the gate used to defer subscribing until a prerequisite is ready.
  • keys - the useMemoized keys. With the default empty list the block runs exactly once.
  • initialData, preserveState - forwarded to useStream; see useStream.

useMemoizedStreamData returns data directly and forwards onError to the built-in error handler.

Use cases

  • Subscribing to a backend stream parameterized by an argument, re-subscribing when that argument changes.

  • Backing a global state with a live stream. The snapshot's connectionState doubles as an "initialized" signal once it reaches ConnectionState.active:

    class OrdersState {
    final bool isInitialized;
    final List<Order>? orders;

    const OrdersState({required this.isInitialized, required this.orders});
    }

    OrdersState useOrdersState(OrderService service, String userId) {
    final snapshot = useMemoizedStream(() => service.streamOrders(userId), keys: [userId]);
    return OrdersState(
    // The stream is "connected and has emitted" once it reaches the active state.
    isInitialized: snapshot.connectionState == ConnectionState.active,
    orders: snapshot.data,
    );
    }

Caveats

  • Put every value the block reads into keys, or the Stream will not re-subscribe when that value changes - the same closure-capture caveat as useMemoized.

  • Like useStream, this exposes only the latest value; intermediate events emitted between builds are lost. For per-event side effects use useStreamSubscription.

See also