注册

跟我学flutter:细细品Widget(一)Widget&Element初识

前言

跟我学flutter系列:
跟我学flutter:我们来举个例子通俗易懂讲解dart 中的 mixin
跟我学flutter:我们来举个例子通俗易懂讲解异步(一)ioslate
跟我学flutter:我们来举个例子通俗易懂讲解异步(二)ioslate循环机制

跟我学flutter:在国内如何发布自己的Plugin 或者 Package

跟我学flutter:Flutter雷达图表(一)如何使用kg_charts

跟我学flutter:细细品Widget(一)Widget&Element初识

企业级篇目:
跟我学企业级flutter项目:用bloc手把手教你搭建用户认证系统
跟我学企业级flutter项目:dio网络框架增加公共请求参数&header
跟我学企业级flutter项目:如何用dio封装一套企业级可扩展高效的网络层
跟我学企业级flutter项目:如何封装一套易用,可扩展的Hybrid混合开发webview
跟我学企业级flutter项目:手把手教你制作一款低耦合空页面widget

Everything's a widget!

Widget

Flutter 中 Widget是一个“描述一个UI元素的配置信息”,Widget就是接受元素,而不是真是绘制的显示元素。 类比原生的Android开发,Widget更像是负责UI配置的xml文件,而非负责绘制组件的View。 当一个Widget状态发生变化时,Widget就会重新调用build()函数来返回控件的描述,过程中Flutter框架会与之前的Widget进行比较,确保实现渲染树中最小的变动来保证性能和稳定性。换句话说,当Widget发生改变时,渲染树只会更新其中的一小部分而非全部重新渲染。

源码

@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });

final Key? key;

@protected
@factory
Element createElement();

@override
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}

@override
@nonVirtual
bool operator ==(Object other) => super == other;

@override
@nonVirtual
int get hashCode => super.hashCode;

static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
...
}

图: 在这里插入图片描述

@immutable

@immutable widget中的属性时不可变的,如果有可变的你需要放在state中。

如果属性发生变更flutter则会重新构建Widget树,一旦 Widget 自己的属性变了自己就会被替换。

如你在开发过程中会有如下提示:This class (or a class that this class inherits from) is marked as '@immutable', but one or more of its instance fields aren't final:

key

主要用于控制当 Widget 更新时,对应的 Element 如何处理 (是更新还是新建)。若某 Widget 是其「Parent Widget」唯一的子节点时,一般不用设置 key

LocalKey

LocalKey是diff算法的核心所在,用做Element和Widget的比较。常用子类有以下几个:

ValueKey:以一个数据为作为key,比如数字、字符等。 ObjectKey:以Object对象作为Key。 UniqueKey:可以保证key的唯一性,如果使用这个类型的key,那么Element对象将不会被复用。 PageStorageKey:用于存储页面滚动位置的key。

GlobalKey

每个globalkey都是一个在整个应用内唯一的key。globalkey相对而言是比较昂贵的,如果你并不需要globalkey的某些特性,那么可以考虑使用Key、ValueKey、ObjectKey或UniqueKey。 他有两个用途:

  1. 允许widget在应用程序中的任何位置更改其parent而不丢失其状态。应用场景:在两个不同的屏幕上显示相同的widget,并保持状态相同。
  2. 可以获取对应Widget的state对象:

createElement

一个 widget 可以对应多个Element

canUpdate

控制一个widget如何替换树中另一个widget。如果两个widget的runtimeType与key相同,则表示新的widget将替换旧的widget,并调用Element.update更新Element;否则旧的element将从树中移出,新的element插入树中。

Widget在重新build的时候,是增量更新的,而不是全部更新 runtimeType就是这个widget的类型

Widget类大家族

在这里插入图片描述

简述(后面文章将展开讲解):

  • StatelessWidget:无状态Widget
  • StatefulWidget:有状态Widget,值得注意的是StatefulWidget是不可变的,变化的状态在。
  • ProxyWidget:其有2个比较重要的子类, ParentDataWidget和InheritedWidget
  • RenderObjectWidget:持有RenderObject对象的Widget,RenderObject是完成界面的布局、测量与绘制,像Padding,Table,Align都是它的子类

Widget的创建可以做到复用,通过const修饰,否则setState后,Widget重新被创建了(Element不会重建)

Element

通过Widget Tree,会生成一系列Element Tree,其主要功能如下:

  1. 维护这棵Element Tree,根据Widget Tree的变化来更新Element Tree,包括:节点的插入、更新、删除、移动等
  2. Element 是 Widget 和 RenderObject 的粘合剂,根据 Element 树生成 Render 树(渲染树)

Element类大家族

在这里插入图片描述

两大类:

简述(后面文章将展开讲解):

ComponentElement

组合类Element。这类Element主要用来组合其他更基础的Element,得到功能更加复杂的Element。开发时常用到的StatelessWidget和StatefulWidget相对应的Element:StatelessElement和StatefulElement,即属于ComponentElement。

RenderObjectElement

渲染类Element,对应Renderer Widget,是框架最核心的Element。RenderObjectElement主要包括LeafRenderObjectElement,SingleChildRenderObjectElement,和MultiChildRenderObjectElement。其中,LeafRenderObjectElement对应的Widget是LeafRenderObjectWidget,没有子节点;SingleChildRenderObjectElement对应的Widget是SingleChildRenderObjectWidget,有一个子节点;MultiChildRenderObjectElement对应的Widget是MultiChildRenderObjecWidget,有多个子节点。

Element生命周期

Element有4种状态:initial,active,inactive,defunct。其对应的意义如下:

  • initial:初始状态,Element刚创建时就是该状态。
  • active:激活状态。此时Element的Parent已经通过mount将该Element插入Element Tree的指定的插槽处(Slot),Element此时随时可能显示在屏幕上。
  • inactive:未激活状态。当Widget Tree发生变化,Element对应的Widget发生变化,同时由于新旧Widget的Key或者的RunTimeType不匹配等原因导致该Element也被移除,因此该Element的状态变为未激活状态,被从屏幕上移除。并将该Element从Element Tree中移除,如果该Element有对应的RenderObject,还会将对应的RenderObject从Render Tree移除。但是,此Element还是有被复用的机会,例如通过GlobalKey进行复用。
  • defunct:失效状态。如果一个处于未激活状态的Element在当前帧动画结束时还是未被复用,此时会调用该Element的unmount函数,将Element的状态改为defunct,并对其中的资源进行清理。

Element4种状态间的转换关系如下图所示:

在这里插入图片描述


0 个评论

要回复文章请先登录注册