Engineering
Flutter State Management: Provider, Riverpod or Bloc?
The most argued-about decision in Flutter, and the one where the wrong choice costs you the most later. A practical guide to picking one.
Start by asking what kind of state you have
Most of the confusion in this debate comes from treating all state as one problem. It isn't, and the answer differs for each kind.
- Ephemeral UI state — is this dropdown open, what's in this text field, which tab is selected. Belongs in a StatefulWidget. No library needed, and reaching for one here is over-engineering.
- Shared app state — the signed-in user, the cart, feature flags. Multiple screens read it, something must own it. This is what the libraries are for.
- Server cache — data you fetched that can go stale. The hardest category, and the one people most often hand-roll badly.
If you can answer "who owns this, and who needs to know when it changes" for every piece of state in your app, the library choice becomes almost incidental. If you can't, no library will save you.
setState — the one people skip too fast
For state that lives in a single widget and its immediate children, setState is correct. It's built in, it's obvious to any Flutter developer, and it has no learning curve.
The failure mode isn't using it — it's lifting it too high. Once you're passing callbacks down four levels to push state back up, you've outgrown it. That's the signal to move, not before.
Provider
The long-standing default, and still a perfectly reasonable choice. It's a thin wrapper over InheritedWidget, which means it's small, well understood, and every Flutter developer you hire will have used it.
Choose it when: the app is small to medium, the team is new to Flutter, or you're inheriting a codebase that already uses it. Its weakness: lookups are by type and resolved at runtime through the widget tree, so a missing provider is a runtime exception rather than a compile error, and testing means building a widget tree.
Riverpod
Written by Provider's author to fix Provider's structural limits. Providers are declared as top-level objects rather than living in the widget tree, which means they're compile-time safe, testable without pumping widgets, and usable from outside the UI entirely.
Choose it when: the app has real complexity, you care about testability, or you have significant async/server state — its AsyncValue handles loading/error/data as one type, which removes a whole class of impossible UI states. Its weakness: more concepts to learn up front, and the ecosystem has changed shape across major versions, so old tutorials mislead.
Bloc
The most structured option: events go in, states come out, and every transition is explicit and observable. That ceremony is the point.
Choose it when: you have complex flows with many transitions — checkout, onboarding, anything regulated — or a large team that benefits from one obvious way to do things, or you need an audit trail of state changes for debugging. Its weakness: significant boilerplate. On a simple screen it's noise, and teams that adopt it everywhere usually end up resenting it.
What we actually pick
For most client projects: Riverpod for app and server state, plain setState for local UI state. That combination gives compile-time safety and clean async handling without ceremony on simple screens.
We use Bloc when a client's domain is genuinely state-machine-shaped — multi-step regulated flows where being able to replay every transition is worth the boilerplate. We keep Provider when we inherit it and it's working; migrating a healthy codebase for fashion is a bad use of a client's money.
The rule that matters more than the choice
Pick one and use it consistently. The worst Flutter codebases we audit aren't the ones that chose "wrong" — they're the ones with Provider in the old screens, Bloc in the checkout, Riverpod in whatever was built last, and a GetX singleton nobody will admit to. Every new engineer has to learn four systems, and state bugs hide in the seams between them.
Any of these three, applied consistently, will carry a production app. Consistency is the decision; the library is a detail.
Building something with Flutter?
We'll tell you honestly whether Flutter is right for it — including when it isn't. Free 30-minute call, no pitch deck.