Home Projects Services Blog Contact
Back to Blog

Flutter state management: Provider, Riverpod or Bloc?

Every Flutter developer runs into this choice. How the three compare, with the same counter written in each.

TL;DR

Provider is simple and a good way to learn the concepts. Riverpod is the more modern option and the one I recommend for new projects. Bloc is more verbose but gives large teams a strict structure. For a new app today, start with Riverpod.

State management is where most Flutter beginners get stuck. Applying one approach consistently matters more than which one you pick, but choosing one that fits the size of your project saves a lot of refactoring later.

What is "state" in Flutter?

State is any data that can change and affects what the UI shows. Whether the user is logged in, how many items are in the cart, whether a button is loading: all of that is state. Managing it means deciding where each piece of data lives and how changes reach the widgets that depend on it.

Provider: the simple option

Provider is a thin layer over Flutter's InheritedWidget, written by Remi Rousselet. The official Flutter docs use it to introduce app state management, and it works well for small and medium apps. It's simple and well documented.

Provider: Counter Example
// 1. Define your state class
class CounterState extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  
  void increment() {
    _count++;
    notifyListeners(); // Tells Flutter to rebuild
  }
}

// 2. Wrap your app with the provider
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CounterState(),
      child: MyApp(),
    ),
  );
}

// 3. Read and update from any widget
Consumer<CounterState>(
  builder: (context, state, _) => Text('${state.count}'),
)
// Update:
context.read<CounterState>().increment();

Riverpod: the modern option

Riverpod comes from the same author and fixes most of Provider's limitations. Providers are declared outside the widget tree, the 'provider not found' errors Provider throws at runtime are caught at compile time, and tests are easier because any provider can be overridden. It's what I recommend for new projects.

Riverpod: Counter with Notifier
import 'package:flutter_riverpod/flutter_riverpod.dart';

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

// 2. Create a provider
final counterProvider = NotifierProvider<CounterNotifier, int>(
  CounterNotifier.new,
);

// 3. Use in widget (ConsumerWidget instead of StatelessWidget)
// The app root must be wrapped once: runApp(ProviderScope(child: MyApp()));
class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Column(children: [
      Text('$count'),
      ElevatedButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: Text('Increment'),
      ),
    ]);
  }
}

Bloc: for large codebases

Bloc (Business Logic Component) separates UI from logic with explicit events and states. You write more boilerplate, but every state change is traceable and easy to test. It's common in large codebases such as banking and e-commerce apps, where many developers work on the same project.

Bloc: Counter Pattern
// Events
abstract class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

// States (just an int here)
// In real apps, states are classes with data

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

// In widget
BlocBuilder<CounterBloc, int>(
  builder: (context, count) => Text('$count'),
)
// Dispatch event:
context.read<CounterBloc>().add(Increment());
Option
  • Provider
  • Riverpod
  • Bloc
Best for
  • Small apps, learning, quick projects
  • New projects, medium to large apps, teams
  • Large apps and teams that want a strict structure

My recommendation: learn the concepts with Provider, then use Riverpod for any serious project. Learn Bloc when a team or a client requires it.

Bottom line

There's no single right answer; it depends on the size of the project and of the team. For a new Flutter app, I'd pick Riverpod: it's light enough for a small app and holds up as the codebase grows.

Tous les articles Article suivant