Sparround

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 setState runs immediately, so awaiting in there is pointless — do the work first, then call setState
  • changing a field without setState does change the value but not the UI; the change then appears "out of nowhere" when the screen later rebuilds for some other reason
MethodWhen it runsWhat it is for
`initState`Once, when the State is createdCreating controllers, opening subscriptions, kicking off the first load
`didChangeDependencies`After `initState`, and whenever an InheritedWidget it depends on changesWork that depends on `Theme.of` or `MediaQuery.of`
`didUpdateWidget`When the parent supplies a new widget of the same typeComparing old and new `widget` parameters and reacting
`dispose`When the State leaves the tree for goodClosing 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