Free tools Windows power users keep installed
One-click scans. No signup required.
Neither Bloc nor Riverpod is the required choice for every Flutter app. Start with the state itself: keep short-lived, widget-specific values local with State and setState; reach for a state-management package when state must be shared, composed across features, persisted, or governed by clearer boundaries. Then choose based on how your team wants to express changes and expose dependencies.
When should you use setState versus a state-management package?
Flutter distinguishes ephemeral state—often local to a widget—from app state that is shared more broadly. The boundary is not fixed: a value that starts in one screen may become app state if other parts of the app need it or it needs to survive navigation. Flutter’s ephemeral-versus-app-state guide explains the distinction.
For a selected tab, an expanded panel, or a temporary text-field value, a widget’s State and setState may be all you need. Flutter’s state-management overview treats built-in state as a valid option, even for a simple whole app; packages are choices, not prerequisites. Its tutorial also demonstrates the separate provider package with ChangeNotifier—that package is not Riverpod.
- Stay local when one widget owns the value and no other feature needs to observe or coordinate it.
- Consider shared state when several screens or services rely on the same value, when business transitions need explicit boundaries, or when app-wide composition and testing require them.
- Reassess as the app changes. Moving a value into shared state is a design decision driven by actual sharing and lifecycle needs, not a rule that every variable belongs in a package.
What is the difference between Bloc and Riverpod?
Bloc and Riverpod organize state differently. In the Bloc family, Cubit exposes methods that change state, while Bloc receives events and maps them to states. Riverpod centers on providers: declarations that act as access points to values or state and can be composed and observed. These are different mental models rather than two names for the same API.
#1 Best Overall
| Choice | How change is expressed | How dependencies and state are accessed | Flutter UI integration |
|---|---|---|---|
| Cubit | Call a method on the Cubit; it emits the resulting state. | flutter_bloc can expose the instance through BlocProvider to a widget subtree. |
Use BlocBuilder to render from state and BlocListener for one-off effects. |
| Bloc | Send an event; the Bloc handles it and emits state. | flutter_bloc can expose the instance through BlocProvider to a widget subtree. |
Use BlocBuilder to render from state and BlocListener for one-off effects. |
| Riverpod | Declare providers and interact with them through the relevant Riverpod API. | Providers serve as access points for shared values and state, and can be composed; in Flutter, place ProviderScope at the root. |
Use the consumer/ref-based observation APIs appropriate to the Riverpod version in the project. |
The descriptions of Bloc and Cubit follow the Bloc concepts documentation. Riverpod’s current provider topic describes provider access, listening, composition, and variants for values, simple state, futures, and streams. For ProviderScope and override concepts, see the explicitly versioned Riverpod v2 guide; use documentation matching your pinned project version rather than assuming every API detail carries forward unchanged.
Should you use Cubit or Bloc?
Choose between them by how you want a transition to read in code. Cubit is method-driven: a caller invokes an operation such as a feature action, and the Cubit produces state. Bloc is event-driven: the caller submits an event, and the Bloc maps that input to output state. The official Bloc concepts guide describes this distinction.
Rank #2
- Use Cubit when direct method calls make the feature’s actions and state transitions clear to your team.
- Use Bloc when you want inputs represented explicitly as events, for example to make the path from a user or lifecycle event to resulting state easier to inspect.
- Keep the choice consistent with the feature and codebase. The documentation supports both patterns; it does not establish one as universally simpler or better.
How do the Flutter UI integrations differ?
With flutter_bloc
BlocProvider makes a Bloc or Cubit available to descendants. When it creates the instance, it owns its lifecycle and closes it automatically; when an existing instance is supplied with BlocProvider.value, it does not take ownership in the same way. BlocBuilder rebuilds UI in response to state and its builder should remain focused on rendering. BlocSelector can select a portion of state so unrelated changes need not trigger that widget’s rebuild; selected values should be immutable. BlocListener is for one-off reactions such as navigation, dialogs, or snackbars, rather than rendering or reacting to the initial state. Use BlocConsumer when a location needs both a builder and a listener. See the Flutter Bloc concepts documentation.
With Riverpod
Riverpod’s provider graph is the organizing mechanism: providers define access points and can depend on other providers. The Riverpod Flutter setup uses ProviderScope at the root, and Flutter widgets observe providers using the consumer/ref APIs available in the version the app uses. Check that version’s documentation for exact widget APIs and async patterns; do not treat Riverpod as interchangeable with the older provider package just because the names resemble each other.
Which is easier to test in Flutter?
Both have documented testing support, and neither source establishes a universal test-speed or test-ease advantage. Bloc’s testing guide shows bloc_test asserting emitted states. Riverpod’s v2 provider guide describes overrides for test scenarios. Evaluate the boundaries your app actually needs to test rather than choosing based on an assumed winner.
- Test important business transitions: inputs, resulting states, and relevant error or loading paths.
- Check dependency boundaries, including repository or service substitutions and provider overrides where applicable.
- Verify lifecycle-sensitive behavior and that UI rebuilds or one-off effects happen at the intended boundary.
- Prefer tests that make the feature’s contract clear over tests that merely mirror framework plumbing.
How should you make the choice for your app?
- Classify the state. If it is confined to one widget, begin with
State/setState. If it is shared, composed, or governed by broader app behavior, compare package approaches. - Sketch one real feature in each mental model. For Bloc, identify methods or events and the states they produce. For Riverpod, identify provider declarations, dependencies, and the widgets or services that observe them.
- Check the UI and side-effect needs. Consider rendering scope, derived selections, navigation, dialogs, and async data handling in the APIs for your installed version.
- Test the boundaries you care about. Exercise transitions, dependency substitutions, lifecycle, and UI response with each library’s documented testing tools.
- Account for the team and existing codebase. Flutter’s own guidance frames the decision around application complexity, team preferences, and the particular problem. Keeping a coherent pattern across a codebase can matter more than adopting a package for fashion.
- Verify package versions against the project. The Bloc site displayed version 9.2.1 when checked for this article, but package versions and compatibility change. Use your lockfile and current package documentation for version and SDK compatibility; no general compatibility matrix or current Riverpod release number is asserted here.
There is no documented universal verdict on boilerplate, performance, popularity, or learning difficulty for this comparison. Treat those as project-specific questions to assess against the feature, team experience, and APIs actually pinned in the app.
Quick Recap
Best Value
Rank #4
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

