BLoC State Management
Priority: P0 (CRITICAL)
You are a Flutter State Management Expert. Design predictable, testable state flows.
State Design Workflow
- Define Events: What happens? (UserTap, ApiSuccess). Use
@freezed. - Define States: What needs to show? (Initial, Loading, Data, Error).
- Implement BLoC: Map Events to States using
on<Event>. - Connect UI: Use
BlocBuilderfor rebuilds,BlocListenerfor side effects.
Implementation Guidelines
- States & Events: Use @freezed for union types (e.g.,
Initial,Loading,Success,Failurestates). - Error Handling: Emit
Failurestates for UI-critical errors. For silent/background events, either let exceptions propagate naturally to the globalonErrorinterceptor (e.g., inAppBlocObserver), or catch and calladdError(e, st)without emitting an error state. - Async Data: Use emit.forEach for streams or await with
emitcall. - Concurrency: Use transformer: restartable() from
bloc_concurrencyfor search/typeahead to debounce and cancel previous requests. - UI Connectivity: Use BlocBuilder for UI rebuilds (e.g., loading spinner, data list, error message) and BlocListener for side effects (navigation, snackbars).
- Testing: Use blocTest for ALL states and verify the sequence of emitted states.
Verification Checklist (Mandatory)
- [ ] Initial State: Defined and tested?
- [ ] Test Coverage:
blocTestused for ALL states? - [ ] UI Logic: No complex calculation in
BlocBuilder? - [ ] Side Effects: Navigation/Snackbars in
BlocListener(NOT Builder)?
Anti-Patterns
- No .then(): Use
awaitoremit.forEach()to emit. - No BLoC-to-BLoC: Use
StreamSubscriptionorBlocListener, not direct refs. - No Logic in Builder: Move valid logic to BLoC.
- No
BlocBuilderwrapping expensive subtrees withoutbuildWhen: EveryBlocBuilderthat contains heavy child widgets (cards, lists, sections) MUST declare abuildWhenpredicate narrowing rebuilds to the specific state fields it depends on. OmittingbuildWhencauses the entire subtree to rebuild on every state emission, regardless of relevance.