Provider vs Bloc
Compare Riverpod and Bloc to choose the state management solution that best fits your application's architecture and team preferences.
Riverpod and Bloc are both mature Flutter state management solutions, but they encourage different approaches to organizing application state.
What is it?
Both Riverpod and Bloc separate business logic from the UI, but they use different programming models.
Riverpod is a provider-based dependency injection and state management framework.
Bloc is an event-driven state management library based on the Business Logic Component (BLoC) pattern.
Both support scalable applications, but they differ in how state changes are represented.
Why does it exist?
Flutter offers multiple state management solutions because applications have different requirements.
Some teams prefer:
- Simple APIs
- Flexible architecture
- Minimal boilerplate
Others prefer:
- Strict architectural rules
- Event-driven workflows
- Highly structured state transitions
Riverpod and Bloc address these different preferences.
Syntax
Riverpod
class CounterNotifier extends Notifier<int> {
@override
int build() => 0;
void increment() {
state++;
}
}
final counterProvider =
NotifierProvider<CounterNotifier, int>(
CounterNotifier.new,
);
Explanation:
- State changes are performed through methods.
- Widgets interact with providers using
ref.watch()andref.read().
Bloc
class IncrementPressed extends CounterEvent {}
class CounterBloc
extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<IncrementPressed>((event, emit) {
emit(state + 1);
});
}
}
Explanation:
- UI dispatches events.
- The Bloc reacts to events and emits new states.
Mental Model
Riverpod:
Widget
│
▼
Notifier
│
▼
State
Bloc:
Widget
│
▼
Event
│
▼
Bloc
│
▼
State
Riverpod calls methods directly, while Bloc routes all changes through events.
Examples
Simple Example
Riverpod
ref.read(counterProvider.notifier).increment();
Explanation:
- The widget calls a method on the notifier.
Bloc
context.read<CounterBloc>()
.add(IncrementPressed());
Explanation:
- The widget dispatches an event.
- The Bloc decides how to handle it.
Real-World Example
Riverpod
class AuthNotifier
extends AsyncNotifier<User?> {
@override
Future<User?> build() async {
return repository.currentUser();
}
Future<void> login(
String email,
String password,
) async {
state = await AsyncValue.guard(() async {
return repository.login(email, password);
});
}
}
Explanation:
- Business logic lives inside the notifier.
- State changes happen through methods.
Bloc
LoginButton
│
▼
LoginRequested Event
│
▼
AuthenticationBloc
│
▼
Loading
│
▼
Authenticated
Explanation:
- Every state transition is triggered by an event.
- The event-driven workflow is explicit.
Feature Comparison
| Feature | Riverpod | Bloc |
|---|---|---|
| Dependency injection | ✅ Built in | ❌ External solution often used |
| Event-based architecture | ❌ | ✅ |
| Business logic classes | ✅ Notifier | ✅ Bloc/Cubit |
| Async state support | ✅ AsyncNotifier | ✅ Bloc |
| Compile-time safety | ✅ | ✅ |
| Boilerplate | Low | Higher |
| Learning curve | Moderate | Moderate to High |
| Flutter dependency | ❌ Can run in pure Dart | ❌ Can run in pure Dart |
When to Use
Choose Riverpod when you want:
- Flexible architecture
- Built-in dependency injection
- Less boilerplate
- Modern provider-based APIs
- Easy testing
- Simple state composition
Choose Bloc when you want:
- Event-driven architecture
- Explicit state transitions
- Strict architectural patterns
- Teams already experienced with Bloc
- Extensive event logging
When NOT to Use
Riverpod may not be the best fit if your team relies heavily on event sourcing or already has a mature Bloc-based architecture.
Bloc may not be the best fit for smaller projects where its additional boilerplate provides little benefit.
The best choice often depends on your team's experience and the application's architectural requirements.
Best Practices
Riverpod
- Keep business logic in notifiers.
- Compose providers instead of nesting logic.
- Use dependency injection through providers.
Bloc
- Keep events focused.
- Emit immutable states.
- Avoid putting business logic inside widgets.
- Keep event handlers small.
Common Mistakes
Choosing Bloc for very simple state
Wrong:
Button
│
Event
│
Bloc
│
State
Explanation:
- The event layer may be unnecessary for simple counters or toggles.
Correct:
Button
│
Notifier
│
State
Explanation:
- Riverpod's notifier API is often simpler for straightforward state changes.
Putting business logic in widgets
Wrong:
onPressed: () async {
await repository.login();
}
Explanation:
- The widget becomes responsible for business logic.
Correct:
ref
.read(authProvider.notifier)
.login(email, password);
Explanation:
- Riverpod centralizes business logic inside the notifier.
Overusing events
Wrong:
IncrementPressed
IncrementCompleted
IncrementFinished
Explanation:
- Extremely granular events can increase complexity without providing additional value.
Correct:
IncrementPressed
Explanation:
- Keep events meaningful and focused.
Related APIs
Riverpod
- Provider
- Notifier
- AsyncNotifier
- ProviderScope
Bloc
- Bloc
- Cubit
- BlocProvider
- BlocBuilder
- BlocListener
Summary
Riverpod and Bloc are both excellent choices for Flutter state management. Riverpod emphasizes providers, dependency injection, and direct method calls with minimal boilerplate, while Bloc focuses on explicit event-driven state transitions and structured workflows. Riverpod is often preferred for its flexibility and simplicity, whereas Bloc is a strong fit for teams that favor strict architectural patterns and event-based design.