What is Bloc?
If you're building scalable Flutter apps and struggling with messy state management, Bloc is one of the most powerful solutions. It helps you write predictable, testable, and maintainable code by separating business logic from the UI. Bloc (Business Logic Component) is a state management library that uses streams to react to events and emit new states. The library flutter_bloc provides widgets to easily integrate Bloc/Cubit into your Flutter app.
Real-World Use Cases
- Login Form – Handle loading, success, and error states with API validation.
- Shopping Cart – Manage items, quantities, totals, and checkout flow.
- Multi‑step Forms – Maintain state across steps (e.g., insurance quote forms).
- API Data Fetching – Show loading indicators, handle errors, and refresh data.
- Authentication – Track user login status and protect routes.
Bloc Data Flow
The flow is unidirectional: UI → Event → Bloc → State → UI. This ensures predictable state changes and makes testing straightforward.
Core Concepts
- Events – Inputs to the Bloc (user actions, lifecycle events).
- States – Outputs from the Bloc representing the current UI state.
- Bloc/Cubit – The component that receives events, transforms them, and emits new states.
- BlocProvider – A widget that provides a Bloc/Cubit to its descendants.
- BlocBuilder – A widget that rebuilds when the state changes.
- BlocListener – A widget that responds to state changes (e.g., show snackbar, navigation).
Cubit vs Bloc
| Feature | Cubit | Bloc |
|---|---|---|
| Event definition | Methods | Events (classes) |
| Boilerplate | Minimal | More |
| Complexity | Simple | Advanced |
| Error handling | Inside methods | Via `on<Event>` |
| Best for | Simple UI state | Complex flows, debouncing, transforms |
Cubit is a simplified version of Bloc that uses methods to emit states directly. Bloc uses event classes and an event handler, which is more verbose but powerful for advanced use cases.
Setting Up flutter_bloc
Add the dependency to your pubspec.yaml:
Run flutter pub get to install.
Creating a Counter Cubit
Let's create a simple counter cubit. We'll define a state class and a cubit that exposes methods to change the state.
Using Cubit in UI
Wrap your widget with BlocProvider to provide the cubit, then use BlocBuilder to rebuild on state changes.
Creating a Bloc (Event-Driven)
For more complex flows, use Bloc with events. Define events, states, and an event handler.
Using Bloc with BlocConsumer
BlocConsumer combines BlocBuilder and BlocListener for both UI rebuilds and side effects.
Best Practices
- Use
Equatable– Override==andhashCodeto avoid unnecessary rebuilds. ExtendEquatablein your states and events. - Keep states immutable – Never mutate a state; always create new instances.
- Use
BlocProvider.valuewisely – Only for existing instances, not for creation inside build. - Use
context.readfor events,context.watchfor state – Prevents unnecessary rebuilds. - Separate business logic – Keep UI pure; move logic to bloc/cubit.
- Handle errors gracefully – Use
onErrorin bloc to catch exceptions.
Common Mistakes
- ❌ Calling
emitoutside the bloc/cubit – Only allowed inside the bloc. - ❌ Not using
Equatable– Causes unnecessary rebuilds because states are considered different even when values are equal. - ❌ Creating bloc inside
build– Recreates on every rebuild, losing state. ✅ UseBlocProvider(create: ...). - ❌ Using
BlocBuilderfor side effects – Should useBlocListener. - ❌ Not disposing controllers/streams – Use
close()or rely on BlocProvider cleanup.
Next Steps
- 👉 Learn more about Cubit in Flutter Cubit Guide
- 👉 Master Bloc Events and Transforms in Advanced Bloc
- 👉 Explore Bloc Architecture for large apps
- 👉 Test your blocs with bloc_test
Conclusion
Bloc is a robust state management solution that enforces separation of concerns and makes your app testable and scalable. Whether you choose the simplicity of Cubit or the power of Bloc with events, flutter_bloc provides a clear pattern for building reactive Flutter applications.