setState and the StatefulWidget lifecycle
setState is not magic — it does exactly two things: it runs your callback synchronously and marks the element dirty. The rebuild itself happens on the next frame.
Two consequences follow:
- the code inside
setStateruns immediately, soawaiting in there is pointless — do the work first, then callsetState - changing a field without
setStatedoes change the value but not the UI; the change then appears "out of nowhere" when the screen later rebuilds for some other reason
| Method | When it runs | What it is for |
|---|---|---|
| `initState` | Once, when the State is created | Creating controllers, opening subscriptions, kicking off the first load |
| `didChangeDependencies` | After `initState`, and whenever an InheritedWidget it depends on changes | Work that depends on `Theme.of` or `MediaQuery.of` |
| `didUpdateWidget` | When the parent supplies a new widget of the same type | Comparing old and new `widget` parameters and reacting |
| `dispose` | When the State leaves the tree for good | Closing controllers, cancelling subscriptions |
Interview tip. Using context in initState is the classic trap: Theme.of(context) and MediaQuery.of(context) are not valid there yet — didChangeDependencies exists for that. Another nuance that comes up: you may start async work from initState, but you must not make initState itself async; extract the work into its own method and call it.
📚 Sources and documentation
- State classofficialapi.flutter.dev
The class docs list the lifecycle call order step by step.
- StatefulWidget classofficialapi.flutter.dev
- Introduction to state managementofficialdocs.flutter.dev