--- id: wiki-2026-0508-bloc title: BLoC category: 10_Wiki/Topics status: verified canonical_id: self aliases: [BLoC, Business Logic Component, flutter_bloc] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [flutter, state-management, dart, reactive] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: dart framework: flutter,flutter_bloc --- # BLoC ## 매 한 줄 > **"매 UI ↔ business logic 의 strict separation — Event in · State out · Stream 으로 의 mediate"**. 2018 Felix Angelov · Paolo Soares Google I/O 의 introduce, 2026 의 flutter_bloc 9.x · Cubit · Hydrated 의 Flutter ecosystem 의 dominant state-management. ## 매 핵심 ### 매 contract - **Event**: input (button tap · API tick). - **State**: output (immutable snapshot). - **Bloc**: `mapEventToState` (legacy) → `on` handler (modern 8.x+). - **Cubit**: simpler — 매 event 의 X, method 의 emit 의 directly. ### 매 component - `BlocProvider` — DI · scope. - `BlocBuilder` — rebuild 의 state change 의 reactive. - `BlocListener` — side-effect (snackbar · navigation). - `BlocConsumer` — builder + listener. - `RepositoryProvider` — data layer DI. ### 매 응용 1. 매 Flutter 의 production app (BMW · Nubank · Alibaba). 2. Form validation (매 reactive validation per field). 3. Authentication flow. 4. Hydrated state (매 disk-persisted, app restart 의 survive). ## 💻 패턴 ### Bloc (event-driven) ```dart sealed class CounterEvent {} class Increment extends CounterEvent {} class Decrement extends CounterEvent {} class CounterBloc extends Bloc { CounterBloc() : super(0) { on((event, emit) => emit(state + 1)); on((event, emit) => emit(state - 1)); } } // usage BlocProvider( create: (_) => CounterBloc(), child: BlocBuilder( builder: (ctx, count) => Text('$count'), ), ) ``` ### Cubit (simpler · method-based) ```dart class CounterCubit extends Cubit { CounterCubit() : super(0); void increment() => emit(state + 1); void decrement() => emit(state - 1); } // trigger context.read().increment(); ``` ### Async event handler with state machine ```dart sealed class LoginState {} class LoginInitial extends LoginState {} class LoginLoading extends LoginState {} class LoginSuccess extends LoginState { final User user; LoginSuccess(this.user); } class LoginFailure extends LoginState { final String msg; LoginFailure(this.msg); } class LoginBloc extends Bloc { final AuthRepository auth; LoginBloc(this.auth) : super(LoginInitial()) { on(_onSubmit); } Future _onSubmit(SubmitLogin e, Emitter emit) async { emit(LoginLoading()); try { final user = await auth.login(e.email, e.password); emit(LoginSuccess(user)); } catch (err) { emit(LoginFailure(err.toString())); } } } ``` ### BlocListener (side-effect) ```dart BlocListener( listener: (ctx, state) { if (state is LoginSuccess) Navigator.pushNamed(ctx, '/home'); if (state is LoginFailure) ScaffoldMessenger.of(ctx).showSnackBar( SnackBar(content: Text(state.msg)), ); }, child: LoginForm(), ) ``` ### Hydrated Bloc (auto-persist) ```dart class SettingsCubit extends HydratedCubit { SettingsCubit() : super(const Settings(theme: 'light')); void setTheme(String t) => emit(state.copyWith(theme: t)); @override Settings fromJson(Map json) => Settings.fromJson(json); @override Map toJson(Settings state) => state.toJson(); } ``` ### Stream-debounced search Bloc ```dart class SearchBloc extends Bloc { SearchBloc(this.api) : super(SearchInitial()) { on( _onSearch, transformer: (events, mapper) => events .debounceTime(const Duration(milliseconds: 300)) .switchMap(mapper), ); } Future _onSearch(SearchQuery e, Emitter emit) async { emit(SearchLoading()); final results = await api.search(e.term); emit(SearchLoaded(results)); } } ``` ### Test (bloc_test) ```dart blocTest( 'emits [1] when Increment is added', build: () => CounterBloc(), act: (b) => b.add(Increment()), expect: () => [1], ); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Simple state · single screen | Cubit | | Complex event flow · undo/redo | Bloc | | Persisted state | HydratedBloc/Cubit | | Server-driven · realtime | Bloc + StreamSubscription | | Multi-screen shared | BlocProvider 의 root | | 매 small project | Provider · Riverpod | **기본값**: Cubit 의 first, Bloc 의 event 의 explicit 의 require 의 case. ## 🔗 Graph - 부모: [[State Management]] - 응용: [[Clean Architecture]] - Adjacent: [[Provider]] · [[Riverpod]] ## 🤖 LLM 활용 **언제**: Bloc · Cubit · Event · State 의 boilerplate 의 generate, blocTest 의 draft. **언제 X**: 매 actual event sequencing · concurrency strategy (debounce/restartable/sequential) — 매 domain 의 understand 의 require. ## ❌ 안티패턴 - **State 의 mutable**: 매 immutable + copyWith 의 mandatory. - **BlocBuilder 의 entire screen 의 wrap**: granular rebuild 의 lose — multiple builder 의 split. - **Bloc 의 navigation 의 directly do**: side-effect 의 BlocListener 의 isolate. - **Singleton bloc 의 manual instantiate**: BlocProvider 의 use — lifecycle 의 leak prevent. ## 🧪 검증 / 중복 - Verified (bloclibrary.dev, flutter_bloc 9.x docs, Felix Angelov GitHub). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Bloc + Cubit + Hydrated + bloc_test patterns |