Widget其实是Element的配置项,Flutter中真正代表屏幕上显示元素的类是Element
。
Tips:Element本身并不处理laying out, painting, 和hit testing这些操作,这些操作是由RenderObject来实现的
Element生命周期
Initial
Element一般并不是直接调用的,而是通过调用Widget.createElement方法来初始化元素,通过阅读Widget源码Element createElement()
,我们可以知道createElement方法返回一个Elment类对象。
由Element Class源码中,我们可以得知,完成了createElement后Element的状态为initial
。
_ElementLifecycle _debugLifecycleState = _ElementLifecycle.initial;
复制代码
Tips:_debugLifecycleState内部属性用于判断当前Element所处的状态。
Active
当RenderObject调用mount方法时,会将新Element添加到Parent的render树中,具体实现:
RenderObject的mount方法先是执行Element的mount方法,根据Element类的mount方法assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
,我们可以得知此时Element的状态已经被改为active
。
因为Element本身不进行layout,所以Elment本身的mount方法只有生命周期的改变,那么layout是在哪里进行的呢?我们可以打开RenderObjectElement的源码,看到mount方法,通过调用attachRenderObject
方法将render object 添加到 render tree中,在此,实现了Element的Layout。
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
_parent = parent;
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;
_active = true;
if (parent != null) // Only assign ownership if the parent is non-null
_owner = parent.owner;
if (widget.key is GlobalKey) {
final GlobalKey key = widget.key;
key._register(this);
}
_updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
}
复制代码
当前状态:active,并且此时已经在屏幕中展示
Inactive
当Parent Rebuilt的时候,Widget将会调用update方法,Flutter会根据runtimeType和key两个属性来判断新旧Widget是否相同,若是相同,则会调用Element.update方法进行更新;若不相同,旧的Element将会从Render Tree中移除,新的Element将会填充进(这一块涉及到Widget的更新、插入、移除操作,这里只是粗略一讲)。那么问题来了,我们能够手动改变runtimeType么?目前来看是不行的,官网也有一句话,
If the parent wishes to change the runtimeType or key of the widget at this location in the tree, can do so by unmounting this element and inflating the new widget at this location.
所以要想改变runtimeType就必须unmounting element。
若Ancestor元素需要将element从tree中移除,Ancestor将会调用deactivateChild方法,此方法将会将element的render object从render tree中移除,并且将该element推入到Ancestor的inactive elements list,在这list中的element将会被调用deactivate方法
当前状态:inactive,并且此时已经从屏幕中移除,此时好像该element还存在于内存中
Defunct
当动画最后一帧结束后,所有inactive的element将会被卸载(unmounted)
void unmount() {
assert(_debugLifecycleState == _ElementLifecycle.inactive);
assert(widget != null);
assert(depth != null);
assert(!_active);
if (widget.key is GlobalKey) {
final GlobalKey key = widget.key;
key._unregister(this);
}
assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; }());
}
复制代码
当前状态:defunct
如果在动画最后一帧结束前该元素被再合并到tree中,框架将会将其从inactive’s list中移除,重新调用该element的activate方法,并且将该element的render object重新推到render tree中
void activate() {
assert(_debugLifecycleState == _ElementLifecycle.inactive);
assert(widget != null);
assert(owner != null);
assert(depth != null);
assert(!_active);
final bool hadDependencies = (_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies;
_active = true;
// We unregistered our dependencies in deactivate, but never cleared the list.
// Since we're going to be reused, let's clear our list now.
_dependencies?.clear();
_hadUnsatisfiedDependencies = false;
_updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
if (_dirty)
owner.scheduleBuildFor(this);
if (hadDependencies)
didChangeDependencies();
}
复制代码
用处
了解了Element的生命周期,那么我们是否能够使用该生命周期,在某些特殊的场景下实现例如组件移除触发的特殊需求呢?
答案当然是可以的,但是有一点需要注意的是,需要借用RenderObjecgtElement或者ComponentElement类来实现,因为这两个类继承自Element类,有人问了,那我直接继承Element类是否可以,这当然也是可以的,但是有一点,正如我最开始说的,Element本身不处理laying out, painting, 和hit testing这些操作,所以这些操作需要你自己实现,而使用RenderObjectElement或者ComponentElement则可以借用其来进行布局渲染等等。
我们都是通过使用Widget来实现功能的,就算我们追本溯源也是继承Widget,那本文讲述的Element又在哪里触发呢?其实上文已经有写了,通过使用Widget的createElement返回Element,那么,所以若是我们想要使用该生命周期,需要创建一个新的类继承自RenderObjectElment或者ComponentElement,使用mount、unmount、activate等方法触发特定的生命周期。
以下代码是我写的一个Demo
class MyWidget extends Widget {
@override
MyElement createElement() => MyElement(this);
}
class MyElement extends ComponentElement {
MyElement(Widget widget) : super(widget);
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
print('触发mount生命周期');
}
@override
void deactivate() {
super.deactivate();
print('触发deactivate生命周期');
}
@override
void unmount() {
super.unmount();
print('触发unmount生命周期');
}
@override
Widget build() {
// TODO: implement build
return Text('测试');
}
}
复制代码
总结
本文讲述了一些特殊名词,例如:Render Tree、RenderObject,这些如果有下篇的话会在下篇中进行详细讲解。