The Flutter Life Cycle
Part 2 in my “Flutter from a complete beginner” series.
Tiger asks … what happens when with Flutter?
Flutter maintains four trees of objects:
- 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 BuildContext
s.
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
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
andunmounted
), 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 theState
, cf. the section about keys in Part 1)State.didChangeDependencies
is called once to initialise and then again when anInheritedWidget
theState
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 tobuild()
).- the decision in
deactivated
whether to reactivate the state or dispose of it happens in the same animation frame as the subtree to which theState
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
RenderObject
s from the render tree and inflating child widgets - a dirtied
State
will mark the underlyingElement
for rebuilding Element.rebuild
will eventually callWidget.build
which forStatefulWidget
s will in turn eventually callState.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, unmountedElements
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.