Line data Source code
1 : import 'dart:async';
2 :
3 : import 'package:bloc/bloc.dart';
4 : import 'package:meta/meta.dart';
5 :
6 : const _asyncRunZoned = runZoned;
7 :
8 : /// {@template bloc_overrides}
9 : /// This class facilitates overriding [BlocObserver] and [EventTransformer].
10 : /// It should be extended by another class in client code with overrides
11 : /// that construct a custom implementation. The implementation in this class
12 : /// defaults to the base [blocObserver] and [eventTransformer] implementation.
13 : /// For example:
14 : ///
15 : /// ```dart
16 : /// class MyBlocObserver extends BlocObserver {
17 : /// ...
18 : /// // A custom BlocObserver implementation.
19 : /// ...
20 : /// }
21 : ///
22 : /// void main() {
23 : /// BlocOverrides.runZoned(() {
24 : /// ...
25 : /// // Bloc instances will use MyBlocObserver instead of the default BlocObserver.
26 : /// ...
27 : /// }, blocObserver: MyBlocObserver());
28 : /// }
29 : /// ```
30 : /// {@endtemplate}
31 : abstract class BlocOverrides {
32 : /// {@macro bloc_overrides}
33 1 : const BlocOverrides();
34 :
35 3 : static final _token = Object();
36 :
37 : /// Returns the current [BlocOverrides] instance.
38 : ///
39 : /// This will return `null` if the current [Zone] does not contain
40 : /// any [BlocOverrides].
41 : ///
42 : /// See also:
43 : /// * [BlocOverrides.runZoned] to provide [BlocOverrides]
44 : /// in a fresh [Zone].
45 : ///
46 1 : static BlocOverrides? get current {
47 3 : return Zone.current[_token] as BlocOverrides?;
48 : }
49 :
50 : /// Runs [body] in a fresh [Zone] using the provided overrides.
51 1 : static R runZoned<R>(
52 : R Function() body, {
53 : BlocObserver? blocObserver,
54 : EventTransformer? eventTransformer,
55 : }) {
56 1 : final overrides = _BlocOverridesScope(blocObserver, eventTransformer);
57 2 : return _asyncRunZoned(body, zoneValues: {_token: overrides});
58 : }
59 :
60 : /// The default [BlocObserver] instance.
61 2 : BlocObserver get blocObserver => Bloc._defaultBlocObserver;
62 :
63 : /// The default [EventTransformer] used for all event handlers.
64 : /// By default all events are processed concurrently.
65 : ///
66 : /// If a custom transformer is specified for a particular event handler,
67 : /// it will take precendence over the global transformer.
68 : ///
69 : /// See also:
70 : ///
71 : /// * [package:bloc_concurrency](https://pub.dev/packages/bloc_concurrency) for an
72 : /// opinionated set of event transformers.
73 : ///
74 2 : EventTransformer get eventTransformer => Bloc._defaultEventTransformer;
75 : }
76 :
77 : class _BlocOverridesScope extends BlocOverrides {
78 1 : _BlocOverridesScope(this._blocObserver, this._eventTransformer);
79 :
80 : final BlocOverrides? _previous = BlocOverrides.current;
81 : final BlocObserver? _blocObserver;
82 : final EventTransformer? _eventTransformer;
83 :
84 1 : @override
85 : BlocObserver get blocObserver {
86 1 : final blocObserver = _blocObserver;
87 : if (blocObserver != null) return blocObserver;
88 :
89 1 : final previous = _previous;
90 1 : if (previous != null) return previous.blocObserver;
91 :
92 1 : return super.blocObserver;
93 : }
94 :
95 1 : @override
96 : EventTransformer get eventTransformer {
97 1 : final eventTransformer = _eventTransformer;
98 : if (eventTransformer != null) return eventTransformer;
99 :
100 1 : final previous = _previous;
101 1 : if (previous != null) return previous.eventTransformer;
102 :
103 1 : return super.eventTransformer;
104 : }
105 : }
106 :
107 : /// {@template emitter}
108 : /// An [Emitter] is a class which is capable of emitting new states.
109 : ///
110 : /// See also:
111 : ///
112 : /// * [EventHandler] which has access to an [Emitter].
113 : ///
114 : /// {@endtemplate}
115 : abstract class Emitter<State> {
116 : /// Subscribes to the provided [stream] and invokes the [onData] callback
117 : /// when the [stream] emits new data.
118 : ///
119 : /// [onEach] completes when the event handler is cancelled or when
120 : /// the provided [stream] has ended.
121 : ///
122 : /// If [onError] is omitted, any errors on this [stream]
123 : /// are considered unhandled, and will be thrown by [onEach].
124 : /// As a result, the internal subscription to the [stream] will be canceled.
125 : ///
126 : /// If [onError] is provided, any errors on this [stream] will be passed on to
127 : /// [onError] and will not result in unhandled exceptions or cancelations to
128 : /// the internal stream subscription.
129 : ///
130 : /// **Note**: The stack trace argument may be [StackTrace.empty]
131 : /// if the [stream] received an error without a stack trace.
132 : Future<void> onEach<T>(
133 : Stream<T> stream, {
134 : required void Function(T data) onData,
135 : void Function(Object error, StackTrace stackTrace)? onError,
136 : });
137 :
138 : // Subscribes to the provided [stream] and invokes the [onData] callback
139 : /// when the [stream] emits new data and the result of [onData] is emitted.
140 : ///
141 : /// [forEach] completes when the event handler is cancelled or when
142 : /// the provided [stream] has ended.
143 : ///
144 : /// If [onError] is omitted, any errors on this [stream]
145 : /// are considered unhandled, and will be thrown by [forEach].
146 : /// As a result, the internal subscription to the [stream] will be canceled.
147 : ///
148 : /// If [onError] is provided, any errors on this [stream] will be passed on to
149 : /// [onError] and will not result in unhandled exceptions or cancelations to
150 : /// the internal stream subscription.
151 : ///
152 : /// **Note**: The stack trace argument may be [StackTrace.empty]
153 : /// if the [stream] received an error without a stack trace.
154 : Future<void> forEach<T>(
155 : Stream<T> stream, {
156 : required State Function(T data) onData,
157 : State Function(Object error, StackTrace stackTrace)? onError,
158 : });
159 :
160 : /// Whether the [EventHandler] associated with this [Emitter]
161 : /// has been completed or canceled.
162 : bool get isDone;
163 :
164 : /// Emits the provided [state].
165 : void call(State state);
166 : }
167 :
168 : /// An event handler is responsible for reacting to an incoming [Event]
169 : /// and can emit zero or more states via the [Emitter].
170 : typedef EventHandler<Event, State> = FutureOr<void> Function(
171 : Event event,
172 : Emitter<State> emit,
173 : );
174 :
175 : /// Signature for a function which converts an incoming event
176 : /// into an outbound stream of events.
177 : /// Used when defining custom [EventTransformer]s.
178 : typedef EventMapper<Event> = Stream<Event> Function(Event event);
179 :
180 : /// Used to change how events are processed.
181 : /// By default events are processed concurrently.
182 : typedef EventTransformer<Event> = Stream<Event> Function(
183 : Stream<Event> events,
184 : EventMapper<Event> mapper,
185 : );
186 :
187 : class _Emitter<State> implements Emitter<State> {
188 1 : _Emitter(this._emit);
189 :
190 : final void Function(State state) _emit;
191 : final _completer = Completer<void>();
192 : final _disposables = <FutureOr<void> Function()>[];
193 :
194 : var _isCanceled = false;
195 : var _isCompleted = false;
196 :
197 : @override
198 1 : Future<void> onEach<T>(
199 : Stream<T> stream, {
200 : required void Function(T data) onData,
201 : void Function(Object error, StackTrace stackTrace)? onError,
202 : }) async {
203 1 : final completer = Completer<void>();
204 1 : final subscription = stream.listen(
205 : onData,
206 1 : onDone: completer.complete,
207 1 : onError: onError ?? completer.completeError,
208 : cancelOnError: onError == null,
209 : );
210 3 : _disposables.add(subscription.cancel);
211 6 : return Future.any([future, completer.future]).whenComplete(() {
212 1 : subscription.cancel();
213 3 : _disposables.remove(subscription.cancel);
214 : });
215 : }
216 :
217 1 : @override
218 : Future<void> forEach<T>(
219 : Stream<T> stream, {
220 : required State Function(T data) onData,
221 : State Function(Object error, StackTrace stackTrace)? onError,
222 : }) {
223 1 : return onEach<T>(
224 : stream,
225 2 : onData: (data) => call(onData(data)),
226 : onError: onError != null
227 1 : ? (Object error, StackTrace stackTrace) {
228 1 : call(onError(error, stackTrace));
229 : }
230 : : null,
231 : );
232 : }
233 :
234 1 : @override
235 : void call(State state) {
236 : assert(
237 2 : !_isCompleted,
238 : '''\n\n
239 : emit was called after an event handler completed normally.
240 : This is usually due to an unawaited future in an event handler.
241 : Please make sure to await all asynchronous operations with event handlers
242 : and use emit.isDone after asynchronous operations before calling emit() to
243 : ensure the event handler has not completed.
244 :
245 : **BAD**
246 : on<Event>((event, emit) {
247 : future.whenComplete(() => emit(...));
248 : });
249 :
250 : **GOOD**
251 : on<Event>((event, emit) async {
252 : await future.whenComplete(() => emit(...));
253 : });
254 : ''',
255 : );
256 2 : if (!_isCanceled) _emit(state);
257 : }
258 :
259 1 : @override
260 2 : bool get isDone => _isCanceled || _isCompleted;
261 :
262 1 : void cancel() {
263 1 : if (isDone) return;
264 1 : _isCanceled = true;
265 1 : _close();
266 : }
267 :
268 1 : void complete() {
269 1 : if (isDone) return;
270 : assert(
271 2 : _disposables.isEmpty,
272 : '''\n\n
273 : An event handler completed but left pending subscriptions behind.
274 : This is most likely due to an unawaited emit.forEach or emit.onEach.
275 : Please make sure to await all asynchronous operations within event handlers.
276 :
277 : **BAD**
278 : on<Event>((event, emit) {
279 : emit.forEach(...);
280 : });
281 :
282 : **GOOD**
283 : on<Event>((event, emit) async {
284 : await emit.forEach(...);
285 : });
286 :
287 : **GOOD**
288 : on<Event>((event, emit) {
289 : return emit.forEach(...);
290 : });
291 :
292 : **GOOD**
293 : on<Event>((event, emit) => emit.forEach(...));
294 :
295 : ''',
296 : );
297 1 : _isCompleted = true;
298 1 : _close();
299 : }
300 :
301 1 : void _close() {
302 2 : for (final disposable in _disposables) disposable.call();
303 2 : _disposables.clear();
304 4 : if (!_completer.isCompleted) _completer.complete();
305 : }
306 :
307 3 : Future<void> get future => _completer.future;
308 : }
309 :
310 : class _Handler {
311 1 : const _Handler({required this.isType, required this.type});
312 : final bool Function(dynamic value) isType;
313 : final Type type;
314 : }
315 :
316 : /// {@template bloc}
317 : /// Takes a `Stream` of `Events` as input
318 : /// and transforms them into a `Stream` of `States` as output.
319 : /// {@endtemplate}
320 : abstract class Bloc<Event, State> extends BlocBase<State> {
321 : /// {@macro bloc}
322 2 : Bloc(State initialState) : super(initialState);
323 :
324 : final _eventController = StreamController<Event>.broadcast();
325 : final _subscriptions = <StreamSubscription<dynamic>>[];
326 : final _handlers = <_Handler>[];
327 : final _emitters = <_Emitter>[];
328 : final _eventTransformer =
329 : BlocOverrides.current?.eventTransformer ?? _defaultEventTransformer;
330 :
331 3 : static final _defaultBlocObserver = BlocObserver();
332 3 : static final _defaultEventTransformer = (Stream events, EventMapper mapper) {
333 : return events
334 1 : .map(mapper)
335 1 : .transform<dynamic>(const _FlatMapStreamTransformer<dynamic>());
336 : };
337 :
338 : /// Notifies the [Bloc] of a new [event] which triggers
339 : /// all corresponding [EventHandler] instances.
340 : ///
341 : /// * A [StateError] will be thrown if there is no event handler
342 : /// registered for the incoming [event].
343 : ///
344 : /// * A [StateError] will be thrown if the bloc is closed and the
345 : /// [event] will not be processed.
346 1 : void add(Event event) {
347 1 : assert(() {
348 4 : final handlerExists = _handlers.any((handler) => handler.isType(event));
349 : if (!handlerExists) {
350 1 : final eventType = event.runtimeType;
351 2 : throw StateError(
352 : '''add($eventType) was called without a registered event handler.\n'''
353 : '''Make sure to register a handler via on<$eventType>((event, emit) {...})''',
354 : );
355 : }
356 : return true;
357 : }());
358 : try {
359 1 : onEvent(event);
360 2 : _eventController.add(event);
361 : } catch (error, stackTrace) {
362 1 : onError(error, stackTrace);
363 : rethrow;
364 : }
365 : }
366 :
367 : /// Called whenever an [event] is [add]ed to the [Bloc].
368 : /// A great spot to add logging/analytics at the individual [Bloc] level.
369 : ///
370 : /// **Note: `super.onEvent` should always be called first.**
371 : /// ```dart
372 : /// @override
373 : /// void onEvent(Event event) {
374 : /// // Always call super.onEvent with the current event
375 : /// super.onEvent(event);
376 : ///
377 : /// // Custom onEvent logic goes here
378 : /// }
379 : /// ```
380 : ///
381 : /// See also:
382 : ///
383 : /// * [BlocObserver.onEvent] for observing events globally.
384 : ///
385 1 : @protected
386 : @mustCallSuper
387 : void onEvent(Event event) {
388 : // ignore: invalid_use_of_protected_member
389 2 : _blocObserver.onEvent(this, event);
390 : }
391 :
392 : /// {@template emit}
393 : /// **[emit] is only for internal use and should never be called directly.
394 : /// The [Emitter] instance provided to each [EventHandler]
395 : /// should be used instead.**
396 : ///
397 : /// ```dart
398 : /// class MyBloc extends Bloc<MyEvent, MyState> {
399 : /// MyBloc() : super(MyInitialState()) {
400 : /// on<MyEvent>((event, emit) {
401 : /// // use `emit` to update the state.
402 : /// emit(MyOtherState());
403 : /// });
404 : /// }
405 : /// }
406 : /// ```
407 : ///
408 : /// Updates the state of the bloc to the provided [state].
409 : /// A bloc's state should only be updated by `emitting` a new `state`
410 : /// from an [EventHandler] in response to an incoming event.
411 : /// {@endtemplate}
412 1 : @internal
413 : @override
414 1 : void emit(State state) => super.emit(state);
415 :
416 : /// Register event handler for an event of type `E`.
417 : /// There should only ever be one event handler per event type `E`.
418 : ///
419 : /// ```dart
420 : /// abstract class CounterEvent {}
421 : /// class Increment extends CounterEvent {}
422 : ///
423 : /// class CounterBloc extends Bloc<CounterEvent, int> {
424 : /// CounterBloc() : super(0) {
425 : /// on<Increment>((event, emit) => emit(state + 1));
426 : /// }
427 : /// }
428 : /// ```
429 : ///
430 : /// * A [StateError] will be thrown if there are multiple event handlers
431 : /// registered for the same type `E`.
432 : ///
433 : /// By default, events will be processed concurrently.
434 : ///
435 : /// See also:
436 : ///
437 : /// * [EventTransformer] to customize how events are processed.
438 : /// * [package:bloc_concurrency](https://pub.dev/packages/bloc_concurrency) for an
439 : /// opinionated set of event transformers.
440 : ///
441 1 : void on<E extends Event>(
442 : EventHandler<E, State> handler, {
443 : EventTransformer<E>? transformer,
444 : }) {
445 1 : assert(() {
446 5 : final handlerExists = _handlers.any((handler) => handler.type == E);
447 : if (handlerExists) {
448 2 : throw StateError(
449 : 'on<$E> was called multiple times. '
450 : 'There should only be a single event handler per event type.',
451 : );
452 : }
453 5 : _handlers.add(_Handler(isType: (dynamic e) => e is E, type: E));
454 : return true;
455 : }());
456 :
457 1 : final _transformer = transformer ?? _eventTransformer;
458 : final subscription = _transformer(
459 6 : _eventController.stream.where((event) => event is E).cast<E>(),
460 1 : (dynamic event) {
461 1 : void onEmit(State state) {
462 1 : if (isClosed) return;
463 3 : if (this.state == state && _emitted) return;
464 2 : onTransition(Transition(
465 1 : currentState: this.state,
466 : event: event as E,
467 : nextState: state,
468 : ));
469 1 : emit(state);
470 : }
471 :
472 1 : final emitter = _Emitter(onEmit);
473 1 : final controller = StreamController<E>.broadcast(
474 : sync: true,
475 1 : onCancel: emitter.cancel,
476 : );
477 :
478 1 : void handleEvent() async {
479 1 : void onDone() {
480 1 : emitter.complete();
481 2 : _emitters.remove(emitter);
482 2 : if (!controller.isClosed) controller.close();
483 : }
484 :
485 : try {
486 2 : _emitters.add(emitter);
487 1 : await handler(event as E, emitter);
488 : } catch (error, stackTrace) {
489 1 : onError(error, stackTrace);
490 : rethrow;
491 : } finally {
492 : onDone();
493 : }
494 : }
495 :
496 : handleEvent();
497 1 : return controller.stream;
498 : },
499 1 : ).listen(null);
500 2 : _subscriptions.add(subscription);
501 : }
502 :
503 : /// Called whenever a [transition] occurs with the given [transition].
504 : /// A [transition] occurs when a new `event` is added
505 : /// and a new state is `emitted` from a corresponding [EventHandler].
506 : /// executed.
507 : /// [onTransition] is called before a [Bloc]'s [state] has been updated.
508 : /// A great spot to add logging/analytics at the individual [Bloc] level.
509 : ///
510 : /// **Note: `super.onTransition` should always be called first.**
511 : /// ```dart
512 : /// @override
513 : /// void onTransition(Transition<Event, State> transition) {
514 : /// // Always call super.onTransition with the current transition
515 : /// super.onTransition(transition);
516 : ///
517 : /// // Custom onTransition logic goes here
518 : /// }
519 : /// ```
520 : ///
521 : /// See also:
522 : ///
523 : /// * [BlocObserver.onTransition] for observing transitions globally.
524 : ///
525 1 : @protected
526 : @mustCallSuper
527 : void onTransition(Transition<Event, State> transition) {
528 : // ignore: invalid_use_of_protected_member
529 2 : _blocObserver.onTransition(this, transition);
530 : }
531 :
532 : /// Closes the `event` and `state` `Streams`.
533 : /// This method should be called when a [Bloc] is no longer needed.
534 : /// Once [close] is called, `events` that are [add]ed will not be
535 : /// processed.
536 : /// In addition, if [close] is called while `events` are still being
537 : /// processed, the [Bloc] will finish processing the pending `events`.
538 : @override
539 : @mustCallSuper
540 1 : Future<void> close() async {
541 3 : await _eventController.close();
542 3 : for (final emitter in _emitters) emitter.cancel();
543 6 : await Future.wait<void>(_emitters.map((e) => e.future));
544 6 : await Future.wait<void>(_subscriptions.map((s) => s.cancel()));
545 1 : return super.close();
546 : }
547 : }
548 :
549 : /// {@template cubit}
550 : /// A [Cubit] is similar to [Bloc] but has no notion of events
551 : /// and relies on methods to [emit] new states.
552 : ///
553 : /// Every [Cubit] requires an initial state which will be the
554 : /// state of the [Cubit] before [emit] has been called.
555 : ///
556 : /// The current state of a [Cubit] can be accessed via the [state] getter.
557 : ///
558 : /// ```dart
559 : /// class CounterCubit extends Cubit<int> {
560 : /// CounterCubit() : super(0);
561 : ///
562 : /// void increment() => emit(state + 1);
563 : /// }
564 : /// ```
565 : ///
566 : /// {@endtemplate}
567 : abstract class Cubit<State> extends BlocBase<State> {
568 : /// {@macro cubit}
569 2 : Cubit(State initialState) : super(initialState);
570 : }
571 :
572 : /// {@template bloc_stream}
573 : /// An interface for the core functionality implemented by
574 : /// both [Bloc] and [Cubit].
575 : /// {@endtemplate}
576 : abstract class BlocBase<State> {
577 : /// {@macro bloc_stream}
578 1 : BlocBase(this._state) {
579 : // ignore: invalid_use_of_protected_member
580 2 : _blocObserver.onCreate(this);
581 : }
582 :
583 : final _blocObserver =
584 : BlocOverrides.current?.blocObserver ?? Bloc._defaultBlocObserver;
585 :
586 : StreamController<State>? __stateController;
587 1 : StreamController<State> get _stateController {
588 2 : return __stateController ??= StreamController<State>.broadcast();
589 : }
590 :
591 : State _state;
592 :
593 : bool _emitted = false;
594 :
595 : /// The current [state].
596 2 : State get state => _state;
597 :
598 : /// The current state stream.
599 3 : Stream<State> get stream => _stateController.stream;
600 :
601 : /// Whether the bloc is closed.
602 : ///
603 : /// A bloc is considered closed once [close] is called.
604 : /// Subsequent state changes cannot occur within a closed bloc.
605 3 : bool get isClosed => _stateController.isClosed;
606 :
607 : /// Updates the [state] to the provided [state].
608 : /// [emit] does nothing if the [state] being emitted
609 : /// is equal to the current [state].
610 : ///
611 : /// To allow for the possibility of notifying listeners of the initial state,
612 : /// emitting a state which is equal to the initial state is allowed as long
613 : /// as it is the first thing emitted by the instance.
614 : ///
615 : /// * Throws a [StateError] if the bloc is closed.
616 1 : @protected
617 : void emit(State state) {
618 : try {
619 1 : if (isClosed) {
620 1 : throw StateError('Cannot emit new states after calling close');
621 : }
622 3 : if (state == _state && _emitted) return;
623 3 : onChange(Change<State>(currentState: this.state, nextState: state));
624 1 : _state = state;
625 3 : _stateController.add(_state);
626 1 : _emitted = true;
627 : } catch (error, stackTrace) {
628 1 : onError(error, stackTrace);
629 : rethrow;
630 : }
631 : }
632 :
633 : /// Called whenever a [change] occurs with the given [change].
634 : /// A [change] occurs when a new `state` is emitted.
635 : /// [onChange] is called before the `state` of the `cubit` is updated.
636 : /// [onChange] is a great spot to add logging/analytics for a specific `cubit`.
637 : ///
638 : /// **Note: `super.onChange` should always be called first.**
639 : /// ```dart
640 : /// @override
641 : /// void onChange(Change change) {
642 : /// // Always call super.onChange with the current change
643 : /// super.onChange(change);
644 : ///
645 : /// // Custom onChange logic goes here
646 : /// }
647 : /// ```
648 : ///
649 : /// See also:
650 : ///
651 : /// * [BlocObserver] for observing [Cubit] behavior globally.
652 : ///
653 1 : @mustCallSuper
654 : void onChange(Change<State> change) {
655 : // ignore: invalid_use_of_protected_member
656 2 : _blocObserver.onChange(this, change);
657 : }
658 :
659 : /// Reports an [error] which triggers [onError] with an optional [StackTrace].
660 1 : @mustCallSuper
661 : void addError(Object error, [StackTrace? stackTrace]) {
662 1 : onError(error, stackTrace ?? StackTrace.current);
663 : }
664 :
665 : /// Called whenever an [error] occurs and notifies [BlocObserver.onError].
666 : ///
667 : /// **Note: `super.onError` should always be called last.**
668 : ///
669 : /// ```dart
670 : /// @override
671 : /// void onError(Object error, StackTrace stackTrace) {
672 : /// // Custom onError logic goes here
673 : ///
674 : /// // Always call super.onError with the current error and stackTrace
675 : /// super.onError(error, stackTrace);
676 : /// }
677 : /// ```
678 1 : @protected
679 : @mustCallSuper
680 : void onError(Object error, StackTrace stackTrace) {
681 : // ignore: invalid_use_of_protected_member
682 2 : _blocObserver.onError(this, error, stackTrace);
683 : }
684 :
685 : /// Closes the instance.
686 : /// This method should be called when the instance is no longer needed.
687 : /// Once [close] is called, the instance can no longer be used.
688 : @mustCallSuper
689 1 : Future<void> close() async {
690 : // ignore: invalid_use_of_protected_member
691 2 : _blocObserver.onClose(this);
692 3 : await _stateController.close();
693 : }
694 : }
695 :
696 : class _FlatMapStreamTransformer<T> extends StreamTransformerBase<Stream<T>, T> {
697 1 : const _FlatMapStreamTransformer();
698 :
699 1 : @override
700 : Stream<T> bind(Stream<Stream<T>> stream) {
701 1 : final controller = StreamController<T>.broadcast(sync: true);
702 :
703 2 : controller.onListen = () {
704 1 : final subscriptions = <StreamSubscription<dynamic>>[];
705 :
706 1 : final outerSubscription = stream.listen(
707 1 : (inner) {
708 1 : final subscription = inner.listen(
709 1 : controller.add,
710 1 : onError: controller.addError,
711 : );
712 :
713 2 : subscription.onDone(() {
714 1 : subscriptions.remove(subscription);
715 1 : if (subscriptions.isEmpty) controller.close();
716 : });
717 :
718 1 : subscriptions.add(subscription);
719 : },
720 1 : onError: controller.addError,
721 : );
722 :
723 2 : outerSubscription.onDone(() {
724 1 : subscriptions.remove(outerSubscription);
725 2 : if (subscriptions.isEmpty) controller.close();
726 : });
727 :
728 1 : subscriptions.add(outerSubscription);
729 :
730 2 : controller.onCancel = () {
731 1 : if (subscriptions.isEmpty) return null;
732 3 : final cancels = [for (final s in subscriptions) s.cancel()];
733 3 : return Future.wait(cancels).then((_) {});
734 : };
735 : };
736 :
737 1 : return controller.stream;
738 : }
739 : }
|