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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值