Flutter -- Element生命周期

Widget实际上是Element的配置项,Flutter中真正表明屏幕上显示元素的类是Elementbash

Tips:Element自己并不处理laying out, painting, 和hit testing这些操做,这些操做是由RenderObject来实现的框架

Element生命周期

Initial

Element通常并非直接调用的,而是经过调用Widget.createElement方法来初始化元素,经过阅读Widget源码Element createElement(),咱们能够知道createElement方法返回一个Elment类对象。ide

由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的状态已经被改成activeui

由于Element自己不进行layout,因此Elment自己的mount方法只有生命周期的改变,那么layout是在哪里进行的呢?咱们能够打开RenderObjectElement的源码,看到mount方法,经过调用attachRenderObject方法将render object 添加到 render tree中,在此,实现了Element的Layout。this

@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,而且此时已经在屏幕中展现spa

Inactive

当Parent Rebuilt的时候,Widget将会调用update方法,Flutter会根据runtimeType和key两个属性来判断新旧Widget是否相同,如果相同,则会调用Element.update方法进行更新;若不相同,旧的Element将会从Render Tree中移除,新的Element将会填充进(这一块涉及到Widget的更新、插入、移除操做,这里只是粗略一讲)。那么问题来了,咱们可以手动改变runtimeType么?目前来看是不行的,官网也有一句话,debug

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,这些若是有下篇的话会在下篇中进行详细讲解。

相关文章
相关标签/搜索