本文主要以下代码介绍flutter中element的生命周期。
1、示例
class TestWidgetLife extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(child: A()),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("我是row1", style: TextStyle(fontSize: 11)),
Text("我是row2", style: TextStyle(fontSize: 11))
],
)
],
);
}
}
class A extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text("我是文本");
}
}
运行结果
2、展示column树结构
可以看到一个widget对应一个element,只有当这个widget需要paint,layout才会对应一个RenderObject。
它们的关系是 widget用于配置element,renderobject用于显示将特定的element配置渲染在屏幕上。
上述只列举了几个element常用的子类。还有其他的比如InheritedWidget中的InheritedElement也是ComponentElement的子类。RootRenderObjectElement是RenderObjectElement的子类。
3、挂载ComponentElement
如下图,以挂载TestWidgetLife为例,左边是挂载流程, 右边这个是StatefulElement的生命期。
TestWidgetLife的element是StatelessElement,只有build方法 ,elment的didUpdateWidget ,activate,deactivate没有回调给使用者。而StatefulElement通过state将生命周期回调给了使用者。
//ComponentElement
void performRebuild() {
Widget? built;
try {
built = build();
} catch (e, stack) {
} finally {
super.performRebuild(); // clears the "dirty" flag
}
try {
_child = updateChild(_child, built, slot);
} catch (e, stack) {
_child = updateChild(null, built, slot);
}
}
//StatefulElement
@override
void deactivate() {
state.deactivate();
super.deactivate();
}
可以看到 performRebuild中,显示调用build方法,也就是我们ComponentElement中的build方法,然后拿着build方法调用updateChild。 此时新widgte(build) 和 oldwidget 对比,是否需要更新或者移除重新加载。
4、挂载RenderObjectElement
上图展示挂载流程,相比较RenderObjectElement,ComponentElement需要调用build方法获取child。而RenderElement没有build方法,通过child参数传递的。所以RenderObject在挂载child时,就直接调用了上图的UpdateChild方法,例如SingleChildRenderObjectElement的mount方法。
//SingleChildRenderObjectElement
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null);
}
//RenderObjectElement
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
attachRenderObject(newSlot);
super.performRebuild(); // clears the "dirty" flag
}
可以看到SingleChildRenderObjectElement 先是创建了createRenderObject,然后添加到卡槽,更新child。
5、刷新ComponentElement
class SwapChildPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _SwapChildState();
}
}
class _SwapChildState extends State<SwapChildPage> {
void _updateTitle() {
setState(() {
widget.title = "文本1-1";
});
}
Widget? myWidget = SwipeStateFulWidget(
// key: GlobalKey(debugLabel: "mahao"),
);
@override
Widget build(BuildContext context) {
print("0-----build");
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
centerTitle: true,
title: Text("交换child")),
body: Align(
child: myWidget,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.delete),
onPressed: () {
setState(() {});
},
),
);
}
}
5.1、先看创建打印
5.2、点击onPressed,执行setState
可以看到只执行了外层的build方法。
5.3、分析
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
// 直接移除child.
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
//如果child为null,而newchild不为空,创建一个新的element加载newWidget。
if (child != null) {
//表示这个child是从外面传进来的,所以同一个引用。
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
//表示新的widget和oldwidget是同一个widget。key和runtype一致。
//只需要更新一下这个widget就可以了,然后重调用build方法。
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
if (isTimelineTracked) {
Map<String, String> debugTimelineArguments = timelineArgumentsIndicatingLandmarkEvent;
child.update(newWidget); //会调用build方法。
newChild = child;
} else {
//如果不是同一个widget,先要移除widget,在加载新的newWidget。
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
//加载新的widget.
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
可以看到当执行到myWidgte的时候,调用updateChild(oldmyWidgetElement, myWidget),按照上面的逻辑,hasSameSuperclass && child.widget == newWidget 类型相同,并且child也是同一个,所以不会更新。
5.4、给align增加UniqueKey()打印如下
body: Align(
key: UniqueKey(),
child: myWidget,
),
5.4.1、UniqueKey表示每次都会创建algin都是唯一的,不同的,所以在更新Align时,每次都会先移除,然后再加载Algin, align下面的子widget也会被移除,也达到移除myWidget的目的。
5.4.2、如上图,可以看到先将原来的myWidget从树上移除,然后将再重新创建一个myWidget对象,挂载到当前树上,最后上移除的旧的myWidget销毁dispose。
5.5、由于上述myWidget是同一个对象,为了测试oldWidget和newWidget不是同一个对象,将myWidget去掉,修改如下。打印。
body: Align(
child: SwipeStateFulWidget(
),
),
此时会调用SwipeStateFulWidget的updateWidget方法和build方法,虽然会强制内部widget build,但是不会重新创建。
5.6、通过globalkey复用之前的widget。
body: Align(
key: UniqueKey(),
child: SwipeStateFulWidget(
key: globalKey,
),
),
Align增加UniqueKey是为了让child重建,SwipeStateFulWidget增加globalkey是为了复用刚才移除的SwipeStateFulWidget。
6、更新RenderObjectElement
//RenderObjectElement
void update(covariant RenderObjectWidget newWidget) {
super.update(newWidget);
_performRebuild(); // calls widget.updateRenderObject()
}
//singleChildRenderObject
@override
void update(SingleChildRenderObjectWidget newWidget) {
super.update(newWidget);
_child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null);
}
也是和mount一样,直接调用了updateChild方法更新子widget。
body: Align(
// key: UniqueKey(),
child: myWidget
),
当调用setState(),更新align,通过build 创建align对象和之前保存的一致,所以调用align的update。看到
align里面的child保存的是SwipeStateFulWidget,newWidget是传递进来的widget。调用调用updateChild也是比较是否需要更新或者移除重新加载。
1、initState
在initState中应该做初始化配置widget的操作。如果widget注册了监听, 需要在didUpdateWidget对old对象取消监听。在dispose中取消监听。
1.1、 当widget被挂载到树中会调用initState方法。
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_firstBuild();
}
void _firstBuild() {
final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
state.didChangeDependencies();
super._firstBuild();
}
2、didChangeDependencies
2.1、首次进入
void _firstBuild() {
final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
state.didChangeDependencies();
super._firstBuild();
}
2.2、状态改变,rebuild的时候调用
@override
void performRebuild() {
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
super.performRebuild();
}
2.3、由不活跃变为活跃状态
void activate() {
final bool hadDependencies = (_dependencies != null && _dependencies!.isNotEmpty) || _hadUnsatisfiedDependencies;
_lifecycleState = _ElementLifecycle.active;
_dependencies?.clear();
_hadUnsatisfiedDependencies = false;
_updateInheritance();
attachNotificationTree();
if (_dirty) {
owner!.scheduleBuildFor(this);
}
if (hadDependencies) {
didChangeDependencies();
}
}
3、didUpdateWidget
当配置发生改变的时候调用,此时必须oldwidget和newWidget的runType和key相同,会强制刷新当前widget。
void update(StatefulWidget newWidget) {
super.update(newWidget);
final StatefulWidget oldWidget = state._widget!;
state._widget = widget as StatefulWidget;
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
rebuild(force: true);
}
4、reassemble
热重载的时候调用,这里应该放一些和initState里面相同的初始化逻辑。
5、build
5.1、调用build()返回widget用于更新当前element的child。
5.2、调用updateChild方法展示更新child的规则。
5.21、原child(element)存在,newWidget为null,直接移除child。
5.22、原child为null,newWidget不为null, 重新为newWidget创建一个element,并且把创建的element挂载到当前的element上。
5.23、如果newWidget和child.widget是同一个对象,比如通过构造函数传递进来的。如果卡槽不一致,更新卡槽即可。
5.24、如果key和runtimeType相同,则执行更新child。调用child的update方法。
对于RenderObjectElement,则会调用,接着继续调用子控件的updateChild方法,直到最后一个控件没有child,也没有element。
@override
void update(SingleChildRenderObjectWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null);
}
对于ComponentElement则会调用下面方法,接着也是调用build方法,然后再更新child,直到最后一个控件没有child也没有element停止。
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
rebuild(force: true);
}
mount的时候,都会为每个widget创建一个element,SingleChildRenderObjectElement和ComponentElement都会把这个element作为成员变量存起来,更新的时候和newWidget比较,是否需要更新。
void performRebuild() {
Widget? built;
try {
built = build();
debugWidgetBuilderValue(widget, built);
} finally {
super.performRebuild(); // clears the "dirty" flag
}
try {
_child = updateChild(_child, built, slot);
} catch (e, stack) {
}
}
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
// 直接移除child.
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
//如果child为null,而newchild不为空,创建一个新的element加载newWidget。
if (child != null) {
//表示这个child是从外面传进来的,所以同一个引用。
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
//表示新的widget和oldwidget是同一个widget。key和runtype一致。
//只需要更新一下这个widget就可以了,然后重调用build方法。
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
if (isTimelineTracked) {
Map<String, String> debugTimelineArguments = timelineArgumentsIndicatingLandmarkEvent;
child.update(newWidget); //会调用build方法。
newChild = child;
} else {
//如果不是同一个widget,先要移除widget,在加载新的newWidget。
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
//加载新的widget.
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
6、deactivate
当widget从树中移除的时候调用。如果重新插入到树中,会调用activate和build方法,这是一个时机去请求一些在deactivate已经释放的资源。
void deactivateChild(Element child) {
child._parent = null;
child.detachRenderObject();
owner!._inactiveElements.add(child); // this eventually calls
}
可以看到,从树上移除的widget对应的element都再加入到_inactiveElements中。
7、activate
Element inflateWidget(Widget newWidget, Object? newSlot) {
final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
try {
final Key? key = newWidget.key;
if (key is GlobalKey) {
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
newChild._activateWithParent(this, newSlot);
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild!;
}
}
final Element newChild = newWidget.createElement();
return newChild;
}
}
@override
void activate() {
super.activate();
state.activate();
markNeedsBuild();
}
可以看到当重建的时候,如果有globalkey,并且缓存的有elements,就直接重用这个element,然后会调用activate方法。
activate还会让当前widget刷新,并且会更新依赖。
8、dispose
当这个widget永久的从这个树上移除时调用。此时mount和unMount方法返回的都是false,此时设置setState会发生错误。子类重载这个方法应该释放资源。
void _unmountAll() {
_locked = true;
final List<Element> elements = _elements.toList()..sort(Element._sort);
_elements.clear();
try {
elements.reversed.forEach(_unmount);
} finally {
assert(_elements.isEmpty);
_locked = false;
}
}
void _unmount(Element element) {
element.visitChildren((Element child) {
_unmount(child);
});
element.unmount();
assert(element._lifecycleState == _ElementLifecycle.defunct);
}
void unmount() {
super.unmount();
state.dispose();
state._element = null;
_state = null;
}
_unmountAll方法是在drawFrame中调用的。可以看到unmount总是先unmount子child,然后自己再unmount。所以dispose总是内部widget先调用dispose。