Skip to content

Intermediate · State management · topic 27 of 50 · 30 min

What state is, and setState

Explanation

State is data that changes and affects what the screen shows. Ephemeral (local) state belongs to one widget, like whether a password is visible; use setState for it. App state is shared by many screens, like the cart or the logged-in user. Lifting state up to a common parent and passing callbacks down works for a few levels, but deep "prop drilling" becomes painful, which is why InheritedWidget and the packages built on it exist.

Two kinds of state

The Flutter docs divide state into two kinds. Ephemeral state (also called UI or local state) lives inside one widget and nobody else cares about it: the current tab of a TabBar, whether a password field shows its text, the progress of an animation. App state is shared across many parts of the app and often needs to survive moving between screens: the shopping cart, the logged-in user, notification settings, a list of favourites.

ExampleKindGood tool
Is the password visible?EphemeralsetState in a StatefulWidget
Selected tab on one screenEphemeralsetState
Items in the cart (shown on many screens)App stateProvider, Riverpod or Bloc
Logged-in user and tokenApp stateProvider, Riverpod or Bloc (plus secure storage)

Lifting state up

When two sibling widgets need the same data, move the state to their closest common parent. The parent owns the data and passes it down as constructor arguments; children report changes upward through callbacks. This "lifting state up" keeps one source of truth.

dart
class CartPage extends StatefulWidget {
  const CartPage({super.key});
  @override
  State<CartPage> createState() => _CartPageState();
}

class _CartPageState extends State<CartPage> {
  int _qty = 1;                                       // state lives in the parent

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        QuantityPicker(qty: _qty, onChanged: (q) => setState(() => _qty = q)),
        PriceSummary(total: _qty * 120),
      ],
    );
  }
}

class QuantityPicker extends StatelessWidget {
  const QuantityPicker({super.key, required this.qty, required this.onChanged});
  final int qty;
  final ValueChanged<int> onChanged;                  // callback up to the parent

  @override
  Widget build(BuildContext context) => Row(children: [
        IconButton(onPressed: qty > 1 ? () => onChanged(qty - 1) : null, icon: const Icon(Icons.remove)),
        Text('$qty'),
        IconButton(onPressed: () => onChanged(qty + 1), icon: const Icon(Icons.add)),
      ]);
}
PriceSummary is a simple StatelessWidget that shows total. Data flows down; events flow up.

The problem: prop drilling

Now imagine the cart count must appear in the AppBar of every screen, the checkout button, and a badge on the bottom navigation bar. Passing cart data and callbacks through every widget in between, even ones that do not use it, is called prop drilling. It makes code long, fragile and hard to change.

InheritedWidget: the foundation

Flutter solves this with InheritedWidget: a widget placed high in the tree that any descendant can read directly through BuildContext, without passing it down by hand. You already use it: Theme.of(context) and MediaQuery.of(context) read InheritedWidgets. Widgets that read it are rebuilt automatically when it changes. Writing InheritedWidgets by hand is verbose, so packages such as Provider wrap it in a friendlier API.

Think of it like this: Prop drilling is like passing a message to the principal by whispering it through every student in the row. InheritedWidget is like the school notice board in the corridor: anyone who needs the notice walks up and reads it, and when it changes, the people who read it are told.

Common mistakes

  • Putting all state in the top-level widget "just in case": every change rebuilds the whole app.
  • Using a state management package for ephemeral state like a checkbox. setState is perfect there.
  • Keeping two copies of the same data in different widgets, which get out of sync.
  • Passing callbacks through five levels of widgets instead of using a shared state tool.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

What state is, and setState · 1/6

State → UI

State is data that changes and affects the screen.

Prefer a video by a teacher? Find free videos on “flutter state management explained ephemeral vs app state lifting state up” on YouTube ↗

In real life: A product page with a quantity picker

On a clothing app's product page, the quantity picker and the price summary both need the quantity. The developer lifts the quantity to the product page's State and passes it down, with an onChanged callback up. When the cart count must also appear on the home screen's bottom bar, she moves the cart to Provider (next topic), keeping setState only for page-local things like the selected size.

Practice (0/3 done)

Task 1 · Question

Which is ephemeral state?

Task 2 · Question

Two sibling widgets need the same counter. What is the simplest correct approach?

Task 3 · Practice

Build a "split the bill" screen: a parent StatefulWidget holds the total and number of people; a stateless PeoplePicker changes the number through a callback; a stateless ShareDisplay shows each person's share. Then list which state in your own app ideas is ephemeral and which is app state.

Quick quiz

Question 1

1.What is app state?

Question 2

2.What is prop drilling?

Question 3

3.Which built-in mechanism do Theme.of and MediaQuery.of use?

Question 4

4.Is setState a bad practice?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is app state?

Answer: State shared by many parts of the app, like a cart

It usually survives navigation.

What is prop drilling?

Answer: Passing data through many widget levels that do not use it

Shared state tools avoid it.

Which built-in mechanism do Theme.of and MediaQuery.of use?

Answer: InheritedWidget

Descendants read inherited data via context.

Is setState a bad practice?

Answer: No, it is the right tool for ephemeral state

Use it for local state.

SearchBar shows the search text and NoteList filters by it. Where should the search text state live?Related

Answer: In NotesPage, their nearest common parent

Lifting state up to the nearest common parent gives one source of truth, passed down as props.

From State: what the screen remembers

What does setState do?Related

Answer: Marks the widget as needing a rebuild after the state changes

Flutter then calls build again.

From Stateless and stateful widgets

How do you pass the number 90 as the price prop?Related

Answer: price={90}

Curly braces pass a JavaScript value. price="90" would pass the string "90", and price=90 is not valid JSX.

From Props and composition

A ProductList and a CartBadge (siblings) both need the cart. Where should the cart state live?Related

Answer: In their closest common parent, passed down as props

Lift state up to the closest common parent, then pass the value and the change functions down. Two copies would get out of sync.

From State and events

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “State management” before its exam: Provider and ChangeNotifier, Riverpod overview, Bloc, Cubit and choosing an approach.
  2. Module 7 exam: State management · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Networking, storage and Firebase. 50 topics left in the course before the final exam.

← Previous: Forms and validation