Flutter 101: 何为 Flutter Elements ?

简介: 0 基础学习 Flutter,第一百零一步:简单了解一下 Element 原理!

      小菜前段时间简单了解了一下 Widget 的相关知识,其中 Widgetimmutable 不可变的,而 Widget 是如何做到更新重绘的,这就离不开 ElementRenderObject;小菜简单了解一下 Element 的相关小知识;

Element

      ElementWidgetUI 树具体位置的一个实例化对象;UI ViewElement 层级结构中会构建一个真实的 Element Tree,是真正的视图树结构;Element 作为 WidgetRenderObject 之间的协调者,并根据 Widget 的变化来完成结点的增删改的操作;

源码分析

      Element 所涉及源码较长,小菜仅针对具体的方法和生命周期进行学习;

生命周期

enum _ElementLifecycle {
  initial,
  active,
  inactive,
  defunct,
}

      Element 的生命周期主要包括如下几分,分别是 initial 初始化,active 活跃状态,inactive 不活跃状态以及 defunct 失效状态;

针对方法

1. createElement
Element(Widget widget) : assert(widget != null), _widget = widget;

      创建一个使用指定 Widget 作为其配置的 Element;通过 Widget 调用 Widget.createElement 来创建 Element,作为 Element 的初始位置;

2. mount
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
    ...
    _parent = parent;
    _slot = newSlot;
    _depth = _parent != null ? _parent.depth + 1 : 1;
    _active = true;
    if (parent != null) 
      _owner = parent.owner;
    if (widget.key is GlobalKey) {
      final GlobalKey key = widget.key;
      key._register(this);
    }
    _updateInheritance();
}

      mount() 会将新创建的 Element 添加到指定的父级 slot 插槽树中,通过调用 attachRenderObject 添加到渲染树上;

3. update
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    if (newWidget == null) {
      if (child != null) deactivateChild(child);
      return null;
    }
    if (child != null) {
      if (child.widget == newWidget) {
        if (child.slot != newSlot) updateSlotForChild(child, newSlot);
        return child;
      }
      if (Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot) updateSlotForChild(child, newSlot);
        child.update(newWidget);
        return child;
      }
      deactivateChild(child);
      assert(child._parent == null);
    }
    return inflateWidget(newWidget, newSlot);
}

      updateChildElement 的核心方法,每当需要增加,修改,删除子 child 时都会调用;主要根据 Widget 的变化用于 Element 的更新,进而更新 UI 树;

newWidget == null newWidget != null
child == null Returns null. Returns new [Element].
child != null Old child is removed, returns null. Old child updated if possible, returns child or new [Element].
  1. 当更新后的 Widgetnull 时,对应的子节点已经移除,如果当前 child 不为 null,则直接 remove 掉;
  2. 当更新后的 Widget 不为 null 且当前 childnull 时,说明新 Widget 是新创建的,则 inflateWidget 创建子节点;
  3. 当更新后的 Widget 不为 null 且当前 child 也不为 null 该节点存在时,若 child.widget == newWidget 说明子节点前后未发生变化,若 child.slot != newSlot 说明子节点在兄弟结点间移动了位置,此时 updateSlotForChild 更新节点位置;否则直接返回子节点;
  4. 当更新后的 Widget 不为 null 且当前 child 也不为 null 该节点存在时,若 Widget.canUpdatetrue 说明可以用 newWidget 修改子节点,直接调用 update 更新即可;否则先将子节点移除再通过 newWidget 创建新的子节点;其中 canUpdate 主要是判断新旧 WidgetkeyruntimeType 是否一致;
4. deactivate
@protected
void deactivateChild(Element child) {
    child._parent = null;
    child.detachRenderObject();
    owner._inactiveElements.add(child); // this eventually calls child.deactivate()
}

      deactivateChild 将指定 Element 已到非活动 Element 列表中,并将渲染对象从渲染树中移除;该方法可以阻止 Element 成为其子类;

5. activate
@mustCallSuper
void activate() {
    final bool hadDependencies = (_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies;
    _active = true;
    _dependencies?.clear();
    _hadUnsatisfiedDependencies = false;
    _updateInheritance();
    
    if (_dirty)
      owner.scheduleBuildFor(this);
    if (hadDependencies)
      didChangeDependencies();
}

      activate() 为将 Element 重新合并到树上时,框架会从 inactive 非活跃 Element 列表中删除该元素,且该元素调用 activate 并将 Element 的渲染对象添加到渲染树上;

6. unmount
@mustCallSuper
void unmount() {
    if (widget.key is GlobalKey) {
        final GlobalKey key = widget.key;
        key._unregister(this);
    }
}

      unmount() 为当框架永远不会重新激活时调用;为了避免在一次动画执行过程中反复创建,移除特定 Element 时,非活跃状态的 Element 都会在当前动画过程最后一帧先保留,如果到动画结束后还未变成活跃状态,则调用 unmount() 将该 Element 彻底移除;

对应关系

  1. Widget.createElementinitial 从无到有的初始化生命周期;
  2. mountinitial 初始化状态到 active 活跃状态到生命周期过渡;
  3. update 只有在 active 活跃状态时才会调用;
  4. deactivateactive 活跃状态到 inactive 非活跃状态生命周期过渡;
  5. activateinactive 非活跃状态到 active 活跃状态的生命周期过渡;
  6. unmountinactive 非活动状态到 defunct 失效状态生命周期的过渡;

子类 Element

      Element 主要有组合类 ComponentElement 和渲染类 RenderObjectElement 两个子类;

ComponentElement

      ComponentElement 为组合类 Element,主要包括如下 StatelessElement / StatefulElement / ProxyElement 子类;其中各 Element 都是与 Widget 对应的;

StatelessElement
class StatelessElement extends ComponentElement {
  StatelessElement(StatelessWidget widget) : super(widget);

  @override
  StatelessWidget get widget => super.widget;

  @override
  Widget build() => widget.build(this);

  @override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _dirty = true;
    rebuild();
  }
}

      StatelessElement 相对比较简单,主要是重写 update() 在需要发生变更时 rebuild() 即可;

StatefulElement
@override
void update(StatefulWidget newWidget) {
    super.update(newWidget);
    final StatefulWidget oldWidget = _state._widget;
    _dirty = true;
    _state._widget = widget;
    try {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
      final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;
    } finally {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
    }
    rebuild();
}

      StatefulElement 是对应 StatefulWidget 的,包括完整的 Element 生命周期,其中在更新时会更新 Staterebuild()

ProxyElement
abstract class ProxyElement extends ComponentElement 
  ProxyElement(ProxyWidget widget) : super(widget);

  @override
  ProxyWidget get widget => super.widget;

  @override
  Widget build() => widget.child;

  @override
  void update(ProxyWidget newWidget) {
    final ProxyWidget oldWidget = widget;
    assert(widget != null);
    assert(widget != newWidget);
    super.update(newWidget);
    assert(widget == newWidget);
    updated(oldWidget);
    _dirty = true;
    rebuild();
  }

  @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  }

  @protected
  void notifyClients(covariant ProxyWidget oldWidget);
}

      ProxyElement 作为一个抽象类,其子类是 ParentDataElementInheritedElement;当 Widget 更新时调用 update()notifyClients() 用于新旧 Widget 确实已改变时调用;

RenderObjectElement

      RenderObjectElement 为渲染类型 Element 对应的是 RenderObjectWidgetRenderObjectElement 作为抽象类也继承了 Element 所有的生命周期方法;

      大多数的 RenderObjectElement 都只对应一个 RenderObject 即只有一个子节点,例如 RootRenderObjectElement / SingleChildRenderObjectElement;但也有特殊的,如 LeafRenderObjectElement 子类没有子节点,以及 MultiChildRenderObjectElement 子类可以有多个子节;


      Element 作为 WidgetRenderObject 的协作者起到了承上启下的左右;小菜对会在下一篇简单学习 RenderObject;小菜对源码的理解还不够深入,如有错误,请多多指导!

来源:阿策小和尚

目录
相关文章
|
2月前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
19天前
|
开发框架 前端开发 测试技术
Flutter开发常见问题解答
Flutter开发常见问题解答
|
2月前
|
前端开发 C++ 容器
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
|
8天前
|
开发框架 移动开发 Android开发
构建高效移动应用:探索Flutter开发框架
【6月更文挑战第28天】随着移动设备的普及,用户对移动应用的需求日益增长。开发者面临着在众多平台间提供无缝体验的挑战。本文深入探讨了Flutter框架如何通过其跨平台特性、热重载功能以及丰富的组件库简化移动应用的开发流程,同时确保高性能和优雅的用户界面设计。
16 2
|
2月前
|
Dart 安全
简化代码、提高效率:Dart和Flutter开发小技巧
在日常开发中,我们常常会使用一些常用的技巧或语法糖,以简化代码、提高开发效率。本文将分享一些在Dart和Flutter中常用的小贴士,帮助你更轻松地编写优雅高效的代码。
简化代码、提高效率:Dart和Flutter开发小技巧
|
26天前
|
Dart 监控 测试技术
在Flutter开发中,注重代码质量与重构实践显得尤为重要
【6月更文挑战第11天】随着Flutter在跨平台开发的普及,保持高质量代码成为开发者关注的重点。良好的代码质量关乎应用性能、稳定性和开发效率。为提升Flutter代码质量,开发者应遵循最佳实践,编写可读性高的代码,实施代码审查和自动化测试。重构实践在应对代码复杂性时也至关重要,包括识别重构时机、制定计划、逐步操作及利用重构工具。注重代码质量和重构是Flutter开发成功的关键。
36 3
|
4天前
|
Dart Android开发 iOS开发
flutter插件开发
flutter插件开发
|
18天前
|
移动开发 小程序 安全
基础入门-APP架构&小程序&H5+Vue语言&Web封装&原生开发&Flutter
基础入门-APP架构&小程序&H5+Vue语言&Web封装&原生开发&Flutter
|
2月前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
|
24天前
|
Dart 前端开发 JavaScript
Flutter for Web:跨平台移动与Web开发的新篇章
Flutter for Web是Google的开源UI工具包Flutter的延伸,用于构建高性能、高保真的跨平台应用,包括Web。它基于Dart语言和Flutter的核心框架,利用Skia渲染引擎通过WebAssembly在Web上运行。开发流程包括安装SDK、创建项目、编写Dart代码和部署。性能优化涉及减少渲染开销、代码压缩等。与传统Web框架相比,Flutter for Web在开发效率和性能上有优势,但兼容性和生态系统尚待完善。
21 0