The Flutter Life Cycle

Part 2 in my “Flutter from a complete beginner” series.

Tiger Asks...
3 min readJan 22, 2024

Tiger asks … what happens when with Flutter?

Flutter maintains four trees of objects:

Fig.1: the three layers of abstraction in Flutter
  • the widget tree of immutable widgets, defining conceptual building blocks
  • the element tree of actual widget instances, created and modified using the definitions in the widget tree
  • the render tree of objects actually displayed on the screen
  • the layer tree defining a paint order for these objects

In addition, each widget has it own BuildContext, and for stateful widgets, the State objects are attached to those BuildContexts.

To make things even more confusing to newcomers, people often don’t differentiate between the trees when talking about things. The documentation acknowledges this issue and explains:

Widgets represent the configuration of Elements.
Each Element has a widget, specified in Element.widget. The term “widget” is often used when strictly speaking “element” would be more correct.

While an Element has a current Widget, over time, that widget may be replaced by others.

And quite honestly, it’s a chore to figure out what exactly happens when.

I asked an established Flutter engineer for advice, and they mentioned the important part to focus on is the state object’s life cycle. So let’s do that.

The State object life cycle

Fig.2: the State object life cycle

The general flow should be self-explanatory. But there’s still some things I believe are worth pointing out:

  • most of the state names (except for mounted and unmounted), I invented so I could build my diagram. don’t expect to see them in official documentation.
  • State.didUpdateWidget gets called when upon rebuild of a parent, a new widget with the same type an key gets placed at the same level as the previous was (same type with different key results in a disposal of the State, cf. the section about keys in Part 1)
  • State.didChangeDependencies is called once to initialise and then again when an InheritedWidget the State is dependent on changes or is moved. As such, it’s well suited for expensive initialisation tasks (i.e. those we don’t want to repeat on each call to build()).
  • the decision in deactivated whether to reactivate the state or dispose of it happens in the same animation frame as the subtree to which the State belongs was unlinked
  • upon reinsertion, the StatefulWidget may have moved to a different position in the widget tree than it was before
  • State.dispose should be responsible for releasing most resources, State.deactivate only cleans up links between the associated element and other elements. Similarly, State.activate should only have to rebuild those links based on the widget’s new position.
  • State.reassemble only gets called during a hot reload in development and “should rerun any initialization logic that depends on global state” (e.g. load image from asset bundle because bundle may have changed)

Cf. the State object documentation if you feel you need more detailed information.

The Full Picture

If we treat widget inflation as a blackbox, Fig.2 does a good job, I feel, at summarising what happens.

I still found it unsatisfying not to know what is actually happening to the elements behind the scenes. So I did a bit of a deeper dive … and let’s just say that rabbit hole goes deep.

Eventually, I gave up on mapping out the entire flutter framework flow, but there’s still some useful bits learned in the failed attempt of doing so:

  • mounting an element in the element tree involves attaching RenderObjects from the render tree and inflating child widgets
  • a dirtied State will mark the underlying Element for rebuilding
  • Element.rebuildwill eventually call Widget.build which for StatefulWidgets will in turn eventually call State.build
  • unlinking an Element already modifies the render tree, re-inserting it when it can be reused will therefore modify the render tree again
  • as with State objects, the decision whether to re-insert or dispose an “unlinked” Element must happen within a single animation frame
  • as with State objects, unmounted Elements are considered defunct and cannot be re-mounted

Most of this “full picture” stuff will not be useful until much, much later in my Flutter journey, but at least Flutter now feels a bit less like a blackbox to me.

--

--

Tiger Asks...

🇨🇭-based Software Engineer with a lot of questions and some answers.