Skip to content

StreamController<T> abstract interface

abstract interface class StreamController<T> implements StreamSink<T>

A controller with the stream it controls.

This controller allows sending data, error and done events on its stream.

This class can be used to create a simple stream that others can listen on, and to push events to that stream.

It's possible to check whether the stream is paused or not, and whether it has subscribers or not, as well as getting a callback when either of these change.

Example:

dart
final streamController = StreamController(
  onPause: () => print('Paused'),
  onResume: () => print('Resumed'),
  onCancel: () => print('Cancelled'),
  onListen: () => print('Listens'),
);

streamController.stream.listen(
  (event) => print('Event: $event'),
  onDone: () => print('Done'),
  onError: (error) => print(error),
);

To check whether there is a subscriber on the stream, use hasListener.

dart
var hasListener = streamController.hasListener; // true

To send data events to the stream, use add or addStream.

dart
streamController.add(999);
final stream = Stream<int>.periodic(
    const Duration(milliseconds: 200), (count) => count * count).take(4);
await streamController.addStream(stream);

To send an error event to the stream, use addError or addStream.

dart
streamController.addError(Exception('Issue 101'));
await streamController.addStream(Stream.error(Exception('Issue 404')));

To check whether the stream is closed, use isClosed.

dart
var isClosed = streamController.isClosed; // false

To close the stream, use close.

dart
await streamController.close();
isClosed = streamController.isClosed; // true

Implemented types

Constructors

StreamController() factory

factory StreamController({
  (void Function())? onListen,
  (void Function())? onPause,
  (void Function())? onResume,
  (FutureOr<void> Function())? onCancel,
  bool sync = false,
})

A controller with a stream that supports only one single subscriber.

If sync is true, the returned stream controller is a SynchronousStreamController, and must be used with the care and attention necessary to not break the Stream contract. If in doubt, use the non-sync version.

Using an asynchronous controller will never give the wrong behavior, but using a synchronous controller incorrectly can cause otherwise correct programs to break.

A synchronous controller is only intended for optimizing event propagation when one asynchronous event immediately triggers another. It should not be used unless the calls to add or addError are guaranteed to occur in places where it won't break Stream invariants.

Use synchronous controllers only to forward (potentially transformed) events from another stream or a future.

A Stream should be inert until a subscriber starts listening on it (using the onListen callback to start producing events). Streams should not leak resources (like websockets) when no user ever listens on the stream.

The controller buffers all incoming events until a subscriber is registered, but this feature should only be used in rare circumstances.

The onPause function is called when the stream becomes paused. onResume is called when the stream resumed.

The onListen callback is called when the stream receives its listener and onCancel when the listener ends its subscription. If onCancel needs to perform an asynchronous operation, onCancel should return a future that completes when the cancel operation is done.

If the stream is canceled before the controller needs data the onResume call might not be executed.

Implementation
dart
factory StreamController({
  void onListen()?,
  void onPause()?,
  void onResume()?,
  FutureOr<void> onCancel()?,
  bool sync = false,
}) {
  return sync
      ? _SyncStreamController<T>(onListen, onPause, onResume, onCancel)
      : _AsyncStreamController<T>(onListen, onPause, onResume, onCancel);
}

StreamController.broadcast() factory

factory StreamController.broadcast({
  (void Function())? onListen,
  (void Function())? onCancel,
  bool sync = false,
})

A controller where stream can be listened to more than once.

The Stream returned by stream is a broadcast stream. It can be listened to more than once.

A Stream should be inert until a subscriber starts listening on it (using the onListen callback to start producing events). Streams should not leak resources (like websockets) when no user ever listens on the stream.

Broadcast streams do not buffer events when there is no listener.

The controller distributes any events to all currently subscribed listeners at the time when add, addError or close is called. It is not allowed to call add, addError, or close before a previous call has returned. The controller does not have any internal queue of events, and if there are no listeners at the time the event or error is added, it will just be dropped.

Each listener subscription is handled independently, and if one pauses, only the pausing listener is affected. A paused listener will buffer events internally until unpaused or canceled.

If sync is true, events may be fired directly by the stream's subscriptions during an add, addError or close call. The returned stream controller is a SynchronousStreamController, and must be used with the care and attention necessary to not break the Stream contract. See Completer.sync for some explanations on when a synchronous dispatching can be used. If in doubt, keep the controller non-sync.

If sync is false, the event will always be fired at a later time, after the code adding the event has completed. In that case, no guarantees are given with regard to when multiple listeners get the events, except that each listener will get all events in the correct order. Each subscription handles the events individually. If two events are sent on an async controller with two listeners, one of the listeners may get both events before the other listener gets any. A listener must be subscribed both when the event is initiated (that is, when add is called) and when the event is later delivered, in order to receive the event.

The onListen callback is called when the first listener is subscribed, and the onCancel is called when there are no longer any active listeners. If a listener is added again later, after the onCancel was called, the onListen will be called again.

Implementation
dart
factory StreamController.broadcast({
  void onListen()?,
  void onCancel()?,
  bool sync = false,
}) {
  return sync
      ? _SyncBroadcastStreamController<T>(onListen, onCancel)
      : _AsyncBroadcastStreamController<T>(onListen, onCancel);
}

Properties

done no setter override

Future<dynamic> get done

A future which is completed when the stream controller is done sending events.

This happens either when the done event has been sent, or if the subscriber on a single-subscription stream is canceled.

A stream controller will not complete the returned future until all listeners present when the done event is sent have stopped listening. A listener will stop listening if it is cancelled, or if it has handled the done event. A paused listener will not process the done even until it is resumed, so completion of the returned Future will be delayed until all paused listeners have been resumed or cancelled.

If there is no listener on a non-broadcast stream, or the listener pauses and never resumes, the done event will not be sent and this future will never complete.

Implementation
dart
Future get done;

hashCode no setter inherited

int get hashCode

The hash code for this object.

A hash code is a single integer which represents the state of the object that affects operator == comparisons.

All objects have hash codes. The default hash code implemented by Object represents only the identity of the object, the same way as the default operator == implementation only considers objects equal if they are identical (see identityHashCode).

If operator == is overridden to use the object state instead, the hash code must also be changed to represent that state, otherwise the object cannot be used in hash based data structures like the default Set and Map implementations.

Hash codes must be the same for objects that are equal to each other according to operator ==. The hash code of an object should only change if the object changes in a way that affects equality. There are no further requirements for the hash codes. They need not be consistent between executions of the same program and there are no distribution guarantees.

Objects that are not equal are allowed to have the same hash code. It is even technically allowed that all instances have the same hash code, but if clashes happen too often, it may reduce the efficiency of hash-based data structures like HashSet or HashMap.

If a subclass overrides hashCode, it should override the operator == operator as well to maintain consistency.

Inherited from Object.

Implementation
dart
external int get hashCode;

hasListener no setter

bool get hasListener

Whether there is a subscriber on the Stream.

Implementation
dart
bool get hasListener;

isClosed no setter

bool get isClosed

Whether the stream controller is closed for adding more events.

The controller becomes closed by calling the close method. New events cannot be added, by calling add or addError, to a closed controller.

If the controller is closed, the "done" event might not have been delivered yet, but it has been scheduled, and it is too late to add more events.

Implementation
dart
bool get isClosed;

isPaused no setter

bool get isPaused

Whether the subscription would need to buffer events.

This is the case if the controller's stream has a listener and it is paused, or if it has not received a listener yet. In that case, the controller is considered paused as well.

A broadcast stream controller is never considered paused. It always forwards its events to all uncanceled subscriptions, if any, and let the subscriptions handle their own pausing and buffering.

Implementation
dart
bool get isPaused;

onCancel read / write

(FutureOr<void> Function())? onCancel

getter:

The callback which is called when the stream is canceled.

May be set to null, in which case no callback will happen.

setter:

The callback which is called when the stream is canceled.

May be set to null, in which case no callback will happen.

Implementation
dart
abstract FutureOr<void> Function()? onCancel;

onListen read / write

(void Function())? onListen

getter:

The callback which is called when the stream is listened to.

May be set to null, in which case no callback will happen.

setter:

The callback which is called when the stream is listened to.

May be set to null, in which case no callback will happen.

Implementation
dart
abstract void Function()? onListen;

onPause read / write

(void Function())? onPause

getter:

The callback which is called when the stream is paused.

May be set to null, in which case no callback will happen.

Pause related callbacks are not supported on broadcast stream controllers.

setter:

The callback which is called when the stream is paused.

May be set to null, in which case no callback will happen.

Pause related callbacks are not supported on broadcast stream controllers.

Implementation
dart
abstract void Function()? onPause;

onResume read / write

(void Function())? onResume

getter:

The callback which is called when the stream is resumed.

May be set to null, in which case no callback will happen.

Pause related callbacks are not supported on broadcast stream controllers.

setter:

The callback which is called when the stream is resumed.

May be set to null, in which case no callback will happen.

Pause related callbacks are not supported on broadcast stream controllers.

Implementation
dart
abstract void Function()? onResume;

runtimeType no setter inherited

Type get runtimeType

A representation of the runtime type of the object.

Inherited from Object.

Implementation
dart
external Type get runtimeType;

sink no setter

StreamSink<T> get sink

Returns a view of this object that only exposes the StreamSink interface.

Implementation
dart
StreamSink<T> get sink;

stream no setter

Stream<T> get stream

The stream that this controller is controlling.

Implementation
dart
Stream<T> get stream;

Methods

add() override

void add(T event)

Sends a data event.

Listeners receive this event in a later microtask.

Note that a synchronous controller (created by passing true to the sync parameter of the StreamController constructor) delivers events immediately. Since this behavior violates the contract mentioned here, synchronous controllers should only be used as described in the documentation to ensure that the delivered events always appear as if they were delivered in a separate microtask.

Implementation
dart
void add(T event);

addError() override

void addError(Object error, [StackTrace? stackTrace])

Sends or enqueues an error event.

Listeners receive this event at a later microtask. This behavior can be overridden by using sync controllers. Note, however, that sync controllers have to satisfy the preconditions mentioned in the documentation of the constructors.

Implementation
dart
void addError(Object error, [StackTrace? stackTrace]);

addStream() override

Future<dynamic> addStream(Stream<T> source, {bool? cancelOnError})

Receives events from source and puts them into this controller's stream.

Returns a future which completes when the source stream is done.

Events must not be added directly to this controller using add, addError, close or addStream, until the returned future is complete.

Data and error events are forwarded to this controller's stream. A done event on the source will end the addStream operation and complete the returned future.

If cancelOnError is true, only the first error on source is forwarded to the controller's stream, and the addStream ends after this. If cancelOnError is false, all errors are forwarded and only a done event will end the addStream. If cancelOnError is omitted or null, it defaults to false.

Implementation
dart
Future addStream(Stream<T> source, {bool? cancelOnError});

close() override

Future<dynamic> close()

Closes the stream.

No further events can be added to a closed stream.

The returned future is the same future provided by done. It is completed when the stream listeners is done sending events, This happens either when the done event has been sent, or when the subscriber on a single-subscription stream is canceled.

A stream controller will not complete the returned future until all listeners present when the done event is sent have stopped listening. A listener will stop listening if it is cancelled, or if it has handled the done event. A paused listener will not process the done even until it is resumed, so completion of the returned Future will be delayed until all paused listeners have been resumed or cancelled.

If no one listens to a non-broadcast stream, or the listener pauses and never resumes, the done event will not be sent and this future will never complete.

Implementation
dart
Future close();

noSuchMethod() inherited

dynamic noSuchMethod(Invocation invocation)

Invoked when a nonexistent method or property is accessed.

A dynamic member invocation can attempt to call a member which doesn't exist on the receiving object. Example:

dart
dynamic object = 1;
object.add(42); // Statically allowed, run-time error

This invalid code will invoke the noSuchMethod method of the integer 1 with an Invocation representing the .add(42) call and arguments (which then throws).

Classes can override noSuchMethod to provide custom behavior for such invalid dynamic invocations.

A class with a non-default noSuchMethod invocation can also omit implementations for members of its interface. Example:

dart
class MockList<T> implements List<T> {
  noSuchMethod(Invocation invocation) {
    log(invocation);
    super.noSuchMethod(invocation); // Will throw.
  }
}
void main() {
  MockList().add(42);
}

This code has no compile-time warnings or errors even though the MockList class has no concrete implementation of any of the List interface methods. Calls to List methods are forwarded to noSuchMethod, so this code will log an invocation similar to Invocation.method(#add, [42]) and then throw.

If a value is returned from noSuchMethod, it becomes the result of the original invocation. If the value is not of a type that can be returned by the original invocation, a type error occurs at the invocation.

The default behavior is to throw a NoSuchMethodError.

Inherited from Object.

Implementation
dart
@pragma("vm:entry-point")
@pragma("wasm:entry-point")
external dynamic noSuchMethod(Invocation invocation);

toString() inherited

String toString()

A string representation of this object.

Some classes have a default textual representation, often paired with a static parse function (like int.parse). These classes will provide the textual representation as their string representation.

Other classes have no meaningful textual representation that a program will care about. Such classes will typically override toString to provide useful information when inspecting the object, mainly for debugging or logging.

Inherited from Object.

Implementation
dart
external String toString();

Operators

operator ==() inherited

bool operator ==(Object other)

The equality operator.

The default behavior for all Objects is to return true if and only if this object and other are the same object.

Override this method to specify a different equality relation on a class. The overriding method must still be an equivalence relation. That is, it must be:

  • Total: It must return a boolean for all arguments. It should never throw.

  • Reflexive: For all objects o, o == o must be true.

  • Symmetric: For all objects o1 and o2, o1 == o2 and o2 == o1 must either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

If a subclass overrides the equality operator, it should override the hashCode method as well to maintain consistency.

Inherited from Object.

Implementation
dart
external bool operator ==(Object other);