DEV Community

Cover image for State Management in Flutter: Riverpod 2.0 vs. Bloc vs. Provider
Bestaoui Aymen
Bestaoui Aymen

Posted on

State Management in Flutter: Riverpod 2.0 vs. Bloc vs. Provider

In 2025, Flutter remains a top choice for cross-platform apps, and choosing the right state management solution is crucial for building scalable, responsive apps. This blog compares three popular options—Riverpod 2.0, Bloc, and Provider—highlighting their strengths, use cases, and a simple example for each.

Why State Management Matters

State management handles data and UI updates in Flutter apps. From simple toggles to complex app-wide data, the right solution ensures clean, maintainable code. Let’s explore Provider, Riverpod 2.0, and Bloc.

1. Provider: Simple and Lightweight

Provider is Flutter’s recommended, beginner-friendly state management library. It’s great for small to medium apps, using ChangeNotifier for reactive updates.

Example: Counter App

import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { runApp(ChangeNotifierProvider(create: (_) => CounterModel(), child: MyApp())); } class CounterModel with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp(home: CounterScreen()); } } class CounterScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Provider Counter')), body: Center(child: Text('Count: ${context.watch<CounterModel>().count}', style: TextStyle(fontSize: 24))), floatingActionButton: FloatingActionButton( onPressed: () => context.read<CounterModel>().increment(), child: Icon(Icons.add), ), ); } } 
Enter fullscreen mode Exit fullscreen mode

Pros & Cons

Pros: Easy to learn, minimal boilerplate, Flutter-endorsed.

Cons: Less scalable for complex apps, limited advanced features.

Best For

Small apps or beginners (e.g., forms, basic UI state).

2. Riverpod 2.0: Modern and Flexible

Riverpod 2.0 (updated in 2024) is a type-safe, dependency-injection-focused evolution of Provider. It’s testable, context-free, and supports advanced features like async state.

Example: Counter App

import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; final counterProvider = StateProvider<int>((ref) => 0); void main() { runApp(ProviderScope(child: MyApp())); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp(home: CounterScreen()); } } class CounterScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(counterProvider); return Scaffold( appBar: AppBar(title: Text('Riverpod Counter')), body: Center(child: Text('Count: $count', style: TextStyle(fontSize: 24))), floatingActionButton: FloatingActionButton( onPressed: () => ref.read(counterProvider.notifier).state++, child: Icon(Icons.add), ), ); } } 
Enter fullscreen mode Exit fullscreen mode

Pros & Cons

Pros: Type-safe, testable, great for async and complex state.

Cons: Steeper learning curve, slightly more boilerplate than Provider.

Best For

Medium to large apps needing scalability and testability.

3. Bloc: Event-Driven and Scalable

Bloc uses an event-driven approach, separating business logic from UI. It’s ideal for large apps with complex state transitions.

Example: Counter App

import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class CounterCubit extends Cubit<int> { CounterCubit() : super(0); void increment() => emit(state + 1); } void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp(home: BlocProvider(create: (_) => CounterCubit(), child: CounterScreen())); } } class CounterScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Bloc Counter')), body: Center(child: BlocBuilder<CounterCubit, int>(builder: (context, count) => Text('Count: $count', style: TextStyle(fontSize: 24)))), floatingActionButton: FloatingActionButton( onPressed: () => context.read<CounterCubit>().increment(), child: Icon(Icons.add), ), ); } } 
Enter fullscreen mode Exit fullscreen mode

Pros & Cons

Pros: Scalable, clear separation of concerns, testable.

Cons: High boilerplate, complex for simple apps.

Best For

Large apps with complex logic (e.g., e-commerce, social apps).

Comparison

Feature Provider Riverpod 2.0 Bloc
Learning Curve Easy Moderate Steep
Boilerplate Low Moderate High
Scalability Small/Medium Medium/Large Large
Type Safety Moderate High High
Async Support Basic Advanced Advanced

Choosing the Right Solution

  • Provider: Best for small apps or quick prototypes.
  • Riverpod 2.0: Ideal for scalable apps with async needs or test-driven development.
  • Bloc: Suited for large, complex apps with strict architecture.

Conclusion

Provider is great for beginners, Riverpod 2.0 offers modern flexibility, and Bloc excels in large-scale apps. Personally, I now use Bloc for all my projects due to its scalability, clear separation of concerns, and robust testing capabilities, making it ideal for complex apps. Try the examples above and share your favorite in the comments!

Top comments (0)