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