Skip to content

State Management

There are a dozen state management packages and a hundred blog wars, but they all answer the same two-part question:

Where does this piece of state live, and which widgets rebuild when it changes?

Everything else — setState, InheritedWidget, Provider, Riverpod, BLoC — is a different mechanism for answering it. Once you see them that way, choosing between them stops being tribal and becomes a matter of scope.

The most useful split is not “which package” but how far the state reaches:

flowchart TB
  eph["Ephemeral state
(one widget: a checkbox,
a text field, an animation)"] --> local["setState
lives in one State object"]
  app["App state
(shared: auth, cart,
settings, cached data)"] --> shared["InheritedWidget / Provider /
Riverpod / BLoC"]
Ephemeral state vs app state
  • Ephemeral (local) state belongs to a single widget and nobody else cares — the current page of a PageView, whether a checkbox is ticked, an animation value. setState is the correct, complete answer. Reaching for a global store here is over-engineering.
  • App (shared) state is needed by many widgets across the tree — the logged-in user, a shopping cart, a theme, data fetched from an API. This is where the dedicated approaches earn their keep.

Most real bugs and most over-engineering come from putting state in the wrong category.

LessonWhat you’ll learn
setState & lifecycleHow rebuilds actually happen, and the State lifecycle
InheritedWidgetThe primitive that makes propagating state down the tree efficient
Provider & RiverpodThe ergonomic, widely used shared-state approaches
BLoC & patternsUnidirectional data flow for complex apps
Choosing an approachA decision guide and how to layer your app
What single question do all state management approaches answer?
What is ephemeral (local) state?
For a simple checkbox that only one widget cares about, what is the right tool?