Skip to content
tezvyn:

Flutter's build(): Why It Lives on State, Not the Widget

Source: api.flutter.devMediumHow cards are made

Flutter's build() method turns state into UI. It's on the State object, not the StatefulWidget, to ensure it always paints with the latest data. The framework calls it on init, after setState(), or when dependencies change.

Why it exists

The build() method exists to provide a function that the Flutter framework can call to construct the visual representation of a widget. It's the core mechanism that translates a widget's configuration and a State object's internal data into a tree of elements that can be rendered on screen.

The mental model

Think of a StatefulWidget as a recipe card (immutable properties) and its State object as the chef. The build() method is the chef's action of cooking the dish. When you want to change the dish (e.g., make it spicier), you give the same persistent chef a new recipe card. The chef then re-runs the build() method, using the new recipe to create an updated UI. The chef (State) persists, but the recipe (Widget) can be replaced.

How it works

The framework calls build() whenever the UI might need to change. This happens after initState, after a call to setState(), when the widget's configuration changes (didUpdateWidget), or when an InheritedWidget it depends on changes. The method must return a Widget. Flutter's engine then efficiently compares this new widget tree to the previous one and updates only what's necessary. For this reason, build() can be called on every frame and must be fast and free of side effects.

When to use it

You don't call build() directly. You must implement it for every State object you create. This is where you define your widget's UI by composing other widgets, using data from both the State object itself and the properties of its corresponding widget (accessed via widget.propertyName).

When not to use it

Never call build() yourself; to request a rebuild, call setState(). Do not perform heavy computations, network requests, or other asynchronous work directly inside build(). It must return a widget synchronously. Offload expensive operations to be triggered by user interactions or in lifecycle methods like initState.

One canonical example

The most critical design choice is that build() is on State, not StatefulWidget. This prevents a subtle bug. If build() were on the widget, a closure inside it (like an onPressed handler) would capture this—the widget instance. If the parent rebuilt the widget with a new property (e.g., a new color), the old closure would still reference the old widget and its old color. By putting build() on State, the closure captures the persistent State object. The framework updates this State object's widget property to point to the new widget instance. Thus, accessing widget.color inside the closure always gets the latest value.

Interview question

According to Flutter's design, what issue would arise if the build() method were placed directly on the StatefulWidget instead of its State object, particularly concerning event handlers?

  • a.The StatefulWidget would lose its ability to hold mutable data, making state management impossible.
  • b.Event handlers would capture the initial StatefulWidget instance, causing them to use outdated properties after a rebuild.Correct
  • c.It would prevent the setState() method from triggering a rebuild of the UI.
  • d.The framework's efficient widget tree comparison algorithm would no longer function correctly.
Why?

The core reason build() is on State is to ensure that closures (like event handlers) capture the persistent State object. This State object's 'widget' property is updated to point to the latest StatefulWidget instance, guaranteeing that any 'widget.propertyName' accessed within the closure always reflects the current data. If build() were on the StatefulWidget, closures would capture the specific (and potentially old) StatefulWidget instance, leading to stale data. Option A is incorrect because StatefulWidgets are designed to be immutable; mutable data is is stored in the associated State object regardless of where build() is defined.

Just read this? Test yourself on what you have been reading.

Read the original → api.flutter.dev

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on flutter — each one lists the topics its interview covers.

See open roles