Line data Source code
1 : // ignore_for_file: library_private_types_in_public_api 2 : 3 : import 'dart:async'; 4 : import 'package:meta/meta.dart'; 5 : 6 : import 'state_notifier.dart'; 7 : 8 : /// A class to allow us implement the BLoC pattern 9 : abstract class Bloc<Event, State> extends StateNotifier<State> { 10 1 : Bloc(super.initialState); 11 : 12 : final _handlers = <_Handler>[]; 13 : 14 : /// Register an event handler for an event of type `E`. 15 : /// There should only ever be one event handler per event type `E`. 16 1 : void on<E extends Event>( 17 : _HandlerFn<E, State> fn, 18 : ) { 19 5 : final registered = _handlers.any((handler) => handler.type == E); 20 : assert( 21 1 : !registered, 22 : 'on<$E> was called multiple times.', // coverage:ignore-line 23 : ); 24 2 : _handlers.add( 25 1 : _Handler( 26 2 : isType: (e) => e is E, 27 : type: E, 28 : fn: fn, 29 : ), 30 : ); 31 : } 32 : 33 : /// Notifies the [Bloc] of a new [event] which triggers 34 : /// all corresponding handlers. 35 : /// 36 : /// * An [AssertionError] will be thrown if there is no event handler 37 : /// registered for the incoming [event]. 38 1 : void add(Event event) { 39 5 : final index = _handlers.indexWhere((e) => e.isType(event)); 40 1 : final eventType = event.runtimeType; 41 : assert( 42 3 : index != -1, 43 1 : 'on<$eventType>(...) must be called before add($eventType)', 44 : ); 45 : 46 3 : final fn = _handlers[index].fn; 47 1 : final emitter = Emitter<State>( 48 2 : (newState) => state = newState, 49 : ); 50 : 51 1 : final result = fn(event, emitter) as FutureOr<void>; 52 : 53 : /// disable the emitter after handler has been completed 54 1 : if (result is Future) { 55 1 : result.then( 56 2 : (_) => emitter._disable(), 57 : ); 58 : } else { 59 1 : emitter._disable(); 60 : } 61 : } 62 : 63 1 : @visibleForTesting 64 : @override 65 1 : set state(State newState) => super.state = newState; 66 : } 67 : 68 : typedef _HandlerFn<E, State> = FutureOr<void> Function( 69 : E event, 70 : Emitter<State> emit, 71 : ); 72 : 73 : class _Handler { 74 1 : const _Handler({ 75 : required this.isType, 76 : required this.type, 77 : required this.fn, 78 : }); 79 : final bool Function(dynamic value) isType; 80 : final Type type; 81 : final Function fn; 82 : } 83 : 84 : /// An [Emitter] is a class which is capable of emitting new states. 85 : class Emitter<State> { 86 1 : Emitter(this._updater); 87 : final void Function(State newState) _updater; 88 : 89 : bool _enabled = true; 90 : 91 1 : void call(State newState) { 92 : assert( 93 1 : _enabled, 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 : **BAD** 98 : on<Event>((event, emit) { 99 : future.whenComplete(() => emit(...)); 100 : }); 101 : **GOOD** 102 : on<Event>((event, emit) async { 103 : await future.whenComplete(() => emit(...)); 104 : }); 105 : ''', 106 : ); 107 2 : _updater(newState); 108 : } 109 : 110 1 : void _disable() { 111 1 : _enabled = false; 112 : } 113 : }