# Riverpod, Bloc and How to Choose — Flutter

Source: https://www.skillbyai.com/en/flutter/s-choose

> Two popular approaches and a decision guide.

## Beyond Provider

**Riverpod** (by Provider's author) declares providers as global, compile-safe objects that do not depend on the widget tree; widgets read them through a `WidgetRef` (`ref.watch`, `ref.read`), and a `ProviderScope` at the root holds the state. It handles async data (`FutureProvider`, `AsyncNotifier` and the `AsyncValue` type), caching and disposal, and is easy to override in tests. **Bloc** (package flutter_bloc) separates UI from logic with **Cubits** (methods that `emit` new states) or **Blocs** (incoming events mapped to states), giving a strict, traceable event flow favoured by large teams. Choose by team and app size: `setState` for local state, Provider for small to medium apps, Riverpod for testable async-heavy apps, Bloc where explicit events and auditability matter. Both libraries have changed APIs across major versions, so check the docs for your version.

## The same counter in Riverpod and Bloc

Notifier with Riverpod 2.x/3.x style; Cubit with flutter_bloc.

```dart
// ---- Riverpod ----
final counterProvider = NotifierProvider<CounterNotifier, int>(CounterNotifier.new);

class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0;
  void increment() => state++;
}

class RiverpodCounter extends ConsumerWidget {
  const RiverpodCounter({super.key});
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return TextButton(
      onPressed: () => ref.read(counterProvider.notifier).increment(),
      child: Text('$count'),
    );
  }
}
// root: runApp(const ProviderScope(child: MyApp()));

// ---- Bloc (Cubit) ----
class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void increment() => emit(state + 1);
}

class BlocCounter extends StatelessWidget {
  const BlocCounter({super.key});
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterCubit, int>(
      builder: (context, count) => TextButton(
        onPressed: () => context.read<CounterCubit>().increment(),
        child: Text('$count'),
      ),
    );
  }
}
// above it: BlocProvider(create: (_) => CounterCubit(), child: ...)
```

## Pick one and be consistent

Every mainstream option can build a large app well. Mixing three state libraries in one codebase is a bigger problem than choosing the "wrong" one.

**Quiz:** In Bloc, how does a Cubit publish a new state?

- [x] By calling emit(newState)
- [ ] By calling notifyListeners()
- [ ] By calling setState()
- [ ] By returning a widget

*Answer:* By calling emit(newState). emit pushes the new state to BlocBuilder and other listeners.
