LCOV - code coverage report
Current view: top level - src - bloc.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 159 160 99.4 %
Date: 2021-10-16 17:36:45 Functions: 0 0 -

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

Generated by: LCOV version 1.15