刀刀网
您的当前位置:首页Flutter -- Element生命周期

Flutter -- Element生命周期

来源:刀刀网

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么?目前来看是不行的,官网也有一句话,

所以要想改变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,这些如果有下篇的话会在下篇中进行详细讲解。

转载于:https://juejin.im/post/5cb975ed6fb9a068b16e1bd0

因篇幅问题不能全部显示,请点此查看更多更全内容