LCOV - code coverage report
Current view: top level - src - bloc.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 158 158 100.0 %
Date: 2021-11-09 23:16:08 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             : 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             : }

Generated by: LCOV version 1.15