Stream<T>.multi constructor Null safety

  1. @Since("2.9")
Stream<T>.multi(
  1. void onListen(
    1. MultiStreamController<T>
    ),
  2. {bool isBroadcast = false}
)

Creates a multi-subscription stream.

Each time the created stream is listened to, the onListen callback is invoked with a new MultiStreamController which forwards events to the StreamSubscription returned by that listen call.

This allows each listener to be treated as an individual stream.

The MultiStreamController does not support reading its StreamController.stream. Setting its StreamController.onListen has no effect since the onListen callback is called instead, and the StreamController.onListen won't be called later. The controller acts like an asynchronous controller, but provides extra methods for delivering events synchronously.

If isBroadcast is set to true, the returned stream's Stream.isBroadcast will be true. This has no effect on the stream behavior, it is up to the onListen function to act like a broadcast stream if it claims to be one.

A multi-subscription stream can behave like any other stream. If the onListen callback throws on every call after the first, the stream behaves like a single-subscription stream. If the stream emits the same events to all current listeners, it behaves like a broadcast stream.

It can also choose to emit different events to different listeners. For example, a stream which repeats the most recent non-null event to new listeners, could be implemented as this example:

extension StreamRepeatLatestExtension<T extends Object> on Stream<T> {
  Stream<T> repeatLatest() {
    var done = false;
    T? latest = null;
    var currentListeners = <MultiStreamController<T>>{};
    this.listen((event) {
      latest = event;
      for (var listener in [...currentListeners]) listener.addSync(event);
    }, onError: (Object error, StackTrace stack) {
      for (var listener in [...currentListeners]) listener.addErrorSync(error, stack);
    }, onDone: () {
      done = true;
      latest = null;
      for (var listener in currentListeners) listener.closeSync();
      currentListeners.clear();
    });
    return Stream.multi((controller) {
      if (done) {
        controller.close();
        return;
      }
      currentListeners.add(controller);
      var latestValue = latest;
      if (latestValue != null) controller.add(latestValue);
      controller.onCancel = () {
        currentListeners.remove(controller);
      };
    });
  }
}

Implementation

@Since("2.9")
factory Stream.multi(void Function(MultiStreamController<T>) onListen,
    {bool isBroadcast = false}) {
  return _MultiStream<T>(onListen, isBroadcast);
}