Flutter state management comes down to two questions: where does changing data live, and who's allowed to trigger a rebuild when it changes. A text field's current value, a shopping cart's line items, whether a toggle is on, all of it is state, and every option from setState to Riverpod is a different answer to those two questions. Widgets rebuild in response to state changes rather than getting updated line by line, the same declarative model Flutter's UI layer is built on that SwiftUI and Jetpack Compose also run on.
The decision gets harder once an app grows past a single screen. The question stops being how to update this widget and becomes which layer of the app owns this data, and how far a change has to travel before the UI reflects it. That's a separate problem from picking a framework in the first place; why teams pick Flutter for a shared codebase in the first place gets decided before state management even comes up.
setState, Provider, Riverpod, Bloc, and signals: how the options actually differ
Flutter ships built-in primitives before any community package enters the picture: setState, ValueNotifier, InheritedWidget, and InheritedModel. Flutter's own state-management options page doesn't rank the packages built on top of them: "The Flutter community offers a wide variety of state management packages. The best choice for your app often depends on the app's complexity, your team's preferences, and the specific problems you need to solve." InheritedWidget, the same page notes, "is what package:provider and many other approaches use under the hood."
Pub.dev listings for Provider, Riverpod, flutter_bloc, and Signals carry the metrics below as of 25 September 2026:
| Package | What it is | When it fits | Pub.dev metrics |
|---|---|---|---|
| setState | Built into every StatefulWidget | Local, single-widget state nobody else needs | Not a package, no pub.dev listing |
| Provider | An InheritedWidget wrapper with a simpler API | App-wide state shared across a moderate widget tree | v6.1.5+1, 11,000 likes, 150 pub points, 1.13M weekly downloads, Flutter Favorite |
| Riverpod | Compile-time-safe rewrite of Provider's ideas, no BuildContext dependency | Apps that want provider errors caught before runtime | v3.4.3, 2,910 likes, 140 pub points, 3.28M weekly downloads |
| flutter_bloc | Event-in, state-out pattern with a strict unidirectional flow | Regulated or audit-heavy apps needing a traceable event log | v9.1.1, 8,080 likes, 160 pub points, 2M weekly downloads |
| Signals | Fine-grained reactive primitives that rebuild only what changed | Apps with frequent, granular updates where full-widget rebuilds get expensive | v7.1.0, 709 likes, 160 pub points, 23.9k weekly downloads |
None of those numbers say which package is best for a given app. They say which one gets downloaded the most (Riverpod) and which one carries Flutter's own Favorite label (Provider). Neither number says which one Flutter's own architecture guide is actually built around.
MVVM in Flutter: the layering Flutter's architecture guide recommends
Flutter's own architecture guide, updated May 5, 2026 for Flutter 3.47, names Model-View-ViewModel directly rather than leaving the pattern question open the way the state-management options page does: "If you've encountered the Model-View-ViewModel architectural pattern (MVVM), this will be familiar. MVVM is an architectural pattern that separates a feature of an application into three parts: the Model, the ViewModel and the View."
Flutter's app-architecture guide goes further than naming the pattern. It recommends four layers, not three: "This guide recommends you split your application into the following components: Views, View models, Repositories, Services." Model is the concept; the guide splits its implementation into two working layers, Repositories and Services, sitting underneath the View and ViewModel that MVVM names directly.
Each layer has a defined job. Views are "the widget classes of your application" and "shouldn't contain any business logic." ViewModels expose "the application data necessary to render a view," and the guide is direct about where the work actually happens: "most of the logic in your Flutter application lives in view models." Repositories are "the source of truth for your model data," pulling from services and turning raw data into domain models. Services sit at "the lowest layer of your application" and wrap API calls, returning Future and Stream objects instead of raw responses.
None of that prescribes which state-management package should fill the ViewModel layer. That's a separate decision, one the guide leaves to whoever's building the app.
How to structure a Flutter app with MVVM
1. Define the View layer
Keep widgets limited to rendering and simple gesture handling. A View reads from a ViewModel and rebuilds when it changes; it never fetches data or calls an API directly. If a widget file imports an HTTP client, that logic belongs one layer down.
2. Build ViewModels that expose state, not widgets
A ViewModel holds the logic and exposes plain data, not Flutter widgets, for the View to render. Flutter's architecture guide puts most of an app's logic here rather than in the View, which is why a ViewModel should be usable without ever touching the widget tree.
3. Add a Repository layer between ViewModels and data
A Repository sits between a ViewModel and one or more Services, transforming raw responses into the domain models a ViewModel actually consumes. That gives the ViewModel a single interface to depend on regardless of how many services back a given feature, and it's the layer that keeps a ViewModel from needing to know whether data came from a REST call, a local database, or a cache.
4. Wire dependency injection (Provider, Riverpod, or GetIt)
ViewModels need Repositories and Services injected rather than constructed inline, so tests can swap them for fakes. Provider and Riverpod both double as dependency-injection mechanisms on top of state management, handing a ViewModel its dependencies through ChangeNotifierProvider or a Riverpod provider. GetIt is a dependency-injection-only option some teams add alongside a state-management package rather than instead of one.
5. Test the ViewModel in isolation from the widget tree
Because ViewModels don't reference widgets, they can be unit tested without pumping a widget tree or running a golden test. Construct the ViewModel directly with fake Repositories, call its methods, and assert on the exposed state. That loop runs faster than a widget test and catches logic bugs before a View ever renders the wrong thing.
Which state-management package pairs best with MVVM
Provider's own model is built on ChangeNotifier, and that pairing is the closest thing Flutter's architecture guide has to a default: a ViewModel that extends ChangeNotifier and calls notifyListeners() when its exposed state changes lines up with what ChangeNotifierProvider expects out of the box.
Riverpod does the same job with compile-time safety instead of runtime lookups. A NotifierProvider or AsyncNotifierProvider plays the ViewModel role without depending on BuildContext, which makes the ViewModel easier to unit test the way step 5 above describes.
Bloc's event-in, state-out flow fits MVVM less directly. It adds an event layer that most ViewModels don't need, but the traceability that comes with it, every state change tied to a named event that caused it, is worth the extra ceremony for regulated or audit-heavy apps.
Signals fits the ViewModel layer at the smallest scale. A ViewModel exposing individual signals instead of one bundled state object lets a View that reads three fields rebuild only for the field that actually changed, rather than for the ViewModel's entire output.
GetX is the outlier. A 2026 roundup of Flutter state-management libraries flags it for maintenance risk tied to a single maintainer, while naming Riverpod, Bloc, and Signals as its stronger picks. GetX also bundles routing and dependency injection into the same package, which cuts against MVVM's separation of a View from everything behind it. GetX's pub.dev listing shows 15.6k likes, more than any other package covered here, alongside a 140 pub points score and 778k weekly downloads, so popularity alone won't settle the question.
What to check when hiring or onboarding Flutter engineers on architecture
Architecture judgment doesn't show up on a CV, and it's a different test than the platform-channel and Impeller questions HighCircl's Flutter hiring guide already covers. Ask a candidate to explain why a ViewModel shouldn't import a Flutter widget, or to justify where they'd draw the boundary between a Repository and a Service on a feature they've actually shipped. A candidate who can only redraw the MVVM diagram hasn't had to defend that boundary under a code review comment that disagrees with it.
The same shift shows up across mobile hiring generally, not just Flutter. What changed in mobile vetting since the Skia-to-Impeller shift applies here too: interview loops that used to stop at a feature checklist now need a live architectural-reasoning component, because a generalist can ship a working screen without being able to explain why the data flows the way it does.
HighCircl's own vetting runs four stages for that reason: background and experience verification, a communication and product-thinking assessment, a take-home technical project, and a live architectural-reasoning session, the stage where a ViewModel-versus-widget boundary question actually gets asked out loud. About 1 in 10 applicants pass all four, and HighCircl ships a shortlist in 72 hours. Senior Flutter rates run €45-105/hr ($50-115/hr), with a flat 20% margin shown as its own line rather than folded into the rate. HighCircl's vetted Flutter engineers are the ones that live interview stage is built to surface.
Frequently asked questions
What is the best state management for Flutter in 2026?
There isn't one universal answer. Flutter's own docs decline to name a favorite, and the right pick depends on app size and how much runtime safety a team wants to trade for setup time: Provider for a small to mid-size app that wants the framework's own default pairing, Riverpod for compile-time safety, Bloc for an auditable event trail, Signals for granular rebuilds.
Does Flutter require MVVM?
No. Flutter runs fine with setState alone on a small app, and plenty of production apps use other patterns. MVVM is what Flutter's own architecture guide recommends once an app has enough features that logic needs a home outside the widget tree, not a requirement the framework enforces.
Can I use Riverpod and MVVM together?
Yes, and it's a common pairing. A Riverpod NotifierProvider or AsyncNotifierProvider can play the ViewModel role directly, exposing state without depending on BuildContext, which keeps the ViewModel testable in isolation the way MVVM expects.
How is Bloc different from MVVM?
MVVM is an architecture pattern describing which layer owns what. Bloc is a state-management package with its own event-in, state-out flow. A Bloc can sit inside the ViewModel layer of an MVVM app, but it adds an event layer that a plain ChangeNotifier-based ViewModel doesn't need.
