Question #249EasyState ManagementImportant

why you choose riverpod , then bloc , provider what is the difference between them ?

#bloc#riverpod#provider

Answer

Overview

Riverpod, BLoC, and Provider are the three most popular Flutter state management solutions. Each has a distinct philosophy and trade-off profile.


Provider (Simple — Beginner Friendly)

dart
// Provider wraps ChangeNotifier
ChangeNotifierProvider(
  create: (_) => CounterModel(),
  child: MyApp(),
)

class CounterModel extends ChangeNotifier {
  int count = 0;
  void increment() { count++; notifyListeners(); }
}

// Consume
final counter = context.watch<CounterModel>();
Text('${counter.count}')

Pros: Simple, small learning curve, great for small apps Cons: Depends on BuildContext, hard to test in isolation, no compile-time safety


BLoC — Predictable, Testable, Strict

dart
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<Increment>((event, emit) => emit(state + 1));
  }
}

BlocBuilder<CounterBloc, int>(
  builder: (context, count) => Text('$count'),
)

Pros: Strict separation of concerns, excellent testability, great for large teams Cons: High boilerplate, steeper learning curve


Riverpod — Compile-Safe, Context-Free, Scalable

dart
final counterProvider = StateNotifierProvider<CounterNotifier, int>(
  (ref) => CounterNotifier(),
);

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);
  void increment() => state++;
}

// Widget
class CounterWidget extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}

Why Riverpod over others:

dart
// 1. No BuildContext needed
final value = ref.read(counterProvider); // Works anywhere

// 2. Compile-time safety — typos caught at compile time
ref.watch(counterProvider); // Not a string key like Get.find<T>()

// 3. Auto-disposal
final userProvider = FutureProvider.autoDispose((ref) => fetchUser());
// Disposed automatically when no longer listened to

// 4. Provider composition
final filteredProvider = Provider((ref) {
  final items = ref.watch(itemsProvider);
  final filter = ref.watch(filterProvider);
  return items.where((i) => i.category == filter).toList();
});

// 5. Family — parameterized providers
final userProvider = FutureProvider.family<User, int>((ref, userId) {
  return fetchUser(userId);
});
ref.watch(userProvider(42)); // Fetch user 42

Full Comparison

FeatureProviderBLoCRiverpod
Learning curve✅ Easy❌ Steep🟡 Moderate
BoilerplateLowHighLow-Medium
Compile safety
Context needed✅ Yes✅ Yes❌ No
TestabilityMedium✅ Excellent✅ Excellent
Auto-dispose❌ Manual❌ Manual✅ Built-in
Async supportManualManual✅ FutureProvider
ScalabilityMedium✅ High✅ High

When to Choose Which

ChooseWhen
ProviderSimple apps, prototypes, beginners
BLoCLarge teams, strict architecture, strong testing culture
RiverpodModern apps, need compile safety, compose state, best DX

Recommendation: Riverpod is the recommended choice for new projects — it addresses Provider's context issues, is compile-safe, supports auto-disposal, and scales to large apps without BLoC's boilerplate.