import 'bloc.dart'; import 'notifier.dart'; /// BAD: `bumpWithoutNotifying` mutates state and never notifies. class StaleNotifier extends ChangeNotifier { int _count = 0; int get count => _count; void bumpWithoutNotifying() { _count = _count + 1; } /// GOOD: notifies directly. void bump() { _count = _count + 1; notifyListeners(); } /// GOOD: mutates, but a notifying method calls it, so the caller announces /// the change. void setQuiet(int next) { _count = next; } void setAndAnnounce(int next) { setQuiet(next); notifyListeners(); } } /// BAD: consumed by a Consumer but has no notifying path at all, so that /// Consumer can never rebuild. class InertNotifier extends ChangeNotifier { int _value = 0; int get value => _value; void setValue(int next) { _value = next; } } /// GOOD: consumed and it notifies. class LiveNotifier extends ChangeNotifier { int _value = 0; int get value => _value; void setValue(int next) { _value = next; notifyListeners(); } } /// Consumed by the UI, but `unusedFlag` is read nowhere: dead state the user can /// never see, while `value` is displayed. class PartiallyReadNotifier extends ChangeNotifier { int _value = 0; bool _unusedFlag = false; int get value => _value; bool get unusedFlag => _unusedFlag; void setValue(int next) { _value = next; notifyListeners(); } } /// A union whose variants are matched ONLY through typed variable patterns /// (`case PatternA value:`), which an object-pattern-only matcher misses. abstract class PatternState { const PatternState(); } class PatternA extends PatternState { const PatternA(); } class PatternB extends PatternState { const PatternB(); } class PatternEvent {} class PatternBloc extends Bloc { PatternBloc() : super(const PatternA()); void go() => emit(const PatternB()); } /// A loading variant emitted on a path whose catch DOES resolve it. The negative /// control for `loading-not-reset`: reporting every loading-emit-then-await would /// flag this cubit, and it must stay silent. abstract class GuardedState { const GuardedState(); } class GuardedLoading extends GuardedState { const GuardedLoading(); } class GuardedReady extends GuardedState { const GuardedReady(); } class GuardedCubit extends Bloc { GuardedCubit() : super(const GuardedReady()); Future load() async { emit(const GuardedLoading()); try { await Future.delayed(Duration.zero); emit(const GuardedReady()); } catch (error) { emit(const GuardedReady()); } } } /// A flag that defaults to FALSE, awaited from the constructor. Nothing is in /// flight, so `loading-flag-not-reset` must stay silent - the negative control /// for that rule, and the regression guard for the missing `defaultsTrue` check. class QuietState { const QuietState({this.loading = false, this.saved = false}); final bool loading; final bool saved; QuietState copyWith({bool? loading, bool? saved}) => QuietState( loading: loading ?? this.loading, saved: saved ?? this.saved, ); } class QuietCubit extends Bloc { QuietCubit() : super(const QuietState()) { _warmUp(); } Future _warmUp() async { await Future.delayed(Duration.zero); } /// Sets the flag in flight and resolves it on every path. Future save() async { emit(state.copyWith(loading: true)); try { await Future.delayed(Duration.zero); emit(state.copyWith(loading: false, saved: true)); } catch (error) { emit(state.copyWith(loading: false)); } } }