美文网首页
Flutter之Widget和Element

Flutter之Widget和Element

作者: gczxbb | 来源:发表于2020-01-18 00:43 被阅读0次

一、Widget

Flutter是移动UI框架,Widget组件是UI的基础。

Widget继承关系图

它们都是抽象类,继承Widget,开发中与StatelessWidget、StatefulWidget类打交道最多,继承两者实现自定义Widget。
1,StatelessWidget,状态不会改变的Widget。

abstract class StatelessWidget extends Widget {
  const StatelessWidget({ Key key }) : super(key: key);
  @override
  StatelessElement createElement() => StatelessElement(this);
  @protected
  Widget build(BuildContext context);
}

build()是抽象方法,每一个类型Widget都对应一种Element,createElement()方法创建。
2,StatefulWidget,状态会改变的Widget组件,需要指定一个State类,根据状态改变时,setState()方法刷新UI,组件会重新build。
定义抽象方法createState()。

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);
  @override
  StatefulElement createElement() => StatefulElement(this);
  @protected
  State createState();
}

抽象类State<T extends StatefulWidget>,提供初始化initState()方法,刷新setState()方法,抽象方法build(),构造Widget,(和StatefulWidget关联)。

build()返回的Widget和它本身的关系:
本质上是由该Widget对应Element的build()方法触发,通过调用Widget或State的build()方法,并没有将当前Widget和返回Widget建立树关系,而是利用返回Widget,为其构建Element,同时mount()挂载,因此,在Flutter中,真正实现树结构的是Element。

3, RenderObjectWidget,三个子类。
LeafRenderObjectWidget,叶子节点。
SingleChildRenderObjectWidget,只有一个Child,包含一个 final Widget child;,子类在构造方法初始化child Widget,例如 Center类。
MultiChildRenderObjectWidget,有多个Child,包含一个 final List<Widget> children;列表,子类在构造方法初始化children数组,例如 Column,Row类。

二、Element

真正的视图树结构

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

基类Element构造方法,初始化Element关联的_widget组件。

Element类继承关系

不同Widget,createElement()方法,创建不同Element,mount()方法也不同,Widget和Element对应关系。

Widget Element
StatelessWidget StatelessElement
StatefulWidget StatefulElement
RenderObjectElement RenderObjectElement
LeafRenderObjectWidget LeafRenderObjectElement
SingleChildRenderObjectWidget SingleChildRenderObjectElement
MultiChildRenderObjectWidget MultiChildRenderObjectElement

RootRenderObjectElement,根节点视图。LeafRenderObjectElement,没有子视图,(叶子节点)。
SingleChildRenderObjectElement,仅有一个子视图。
MultiChildRenderObjectElement,有多个子视图。

1,StatelessElement和StatefulElement类
class StatelessElement extends ComponentElement {
  StatelessElement(StatelessWidget widget) : super(widget);
  @override
  StatelessWidget get widget => super.widget;
  @override
  Widget build() => widget.build(this);
  ...
}

build()方法,调用关联widget的build()方法,返回Widget。

class StatefulElement extends ComponentElement {
    StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
    _state._element = this;
    _state._widget = widget;
}

StatefulElement构造方法,参数StatefulWidget组件,createState()方法,初始化State对象,State关联Element和StatefulWidget,
StatefulElement类的build()方法,调用关联State的build()方法,返回Widget。

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

State的build()方法,参数this,(BuildContext类型),其实,Element实现BuildContext。

开发者重写StatelessWidget或State<T extends StatefulWidget>的build()方法,(返回Widget),都是由Element负责调用。

2,ComponentElement的挂载

StatelessElement和StatefulElement类都继承ComponentElement,它们的挂载由基础ComponentElement的mount()挂载方法。
mount的意思是将这个节点挂到树上去。

@override
void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _firstBuild();
}

super.mount()基类Element的挂载方法,即初始化_parent = parent;,指将该节点指向入参父Element。
调用流程:_firstBuild() == > rebuild() == > performRebuild()。
(performRebuild是Element的抽象类,如下ComponentElement类实现)。

@override
void performRebuild() {
  Widget built;
  try {
    //调用widget的build或state的build。
    built = build();
  } catch (e, stack) {
  } 
  try {
    //将调用inflateWidget()方法,_child:内部的子Element。
    _child = updateChild(_child, built, slot);
    assert(_child != null);
  } catch (e, stack) {
  }
}

通过build()方法,创建Widget,根据不同类型Element,Widget或State的build()方法,(即开发重写的即该方法)。
新创建的Widget(built),作为该Element子节点的配置,updateChild()方法,初始化子Element节点,ComponentElement类保存一份Element _child;。
Element基类的inflateWidget(),根据Widget构造Element。

@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
  final Key key = newWidget.key;
  if (key is GlobalKey) {
    final Element newChild = _retakeInactiveElement(key, newWidget);
    if (newChild != null) {
      newChild._activateWithParent(this, newSlot);
      final Element updatedChild = updateChild(newChild, newWidget, newSlot);
      return updatedChild;
    }
  }
  //调用widget自己重写的createElement()方法
  final Element newChild = newWidget.createElement();
  //挂载
  newChild.mount(this, newSlot);
  return newChild;
}

创建的子Element节点挂载mount到自己上,this即内部_parent,Element是树形结构,每一个视图对应Element。

ComponentElement类挂载mount(),将触发build方法,构建widget对象,再去inflateWidget(),为Widget生成Element,挂载,初始化构建Element树。

3,RenderObjectElement子类的挂载

Element类基础mount()方法,将内部_parent设置父视图Element。
SingleChildRenderObjectElement类,有一个子元素。

@override
void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _child = updateChild(_child, widget.child, null);
}

super.mount()挂载,初始化_parent和内部_child元素,根据widget的child,和ComponentElement挂载类似(build方法产生的子Widget),updateChild()方法直接处理子Widget,(widget.child),生成_child元素。
(widget对应SingleChildRenderObjectWidget,内部child Widget。)
MultiChildRenderObjectElement类,有多个子元素。

@override
void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _children = List<Element>(widget.children.length);
  Element previousChild;
  for (int i = 0; i < _children.length; i += 1) {
    final Element newChild = inflateWidget(widget.children[i], previousChild);
    _children[i] = newChild;
    previousChild = newChild;
  }
}

遍历该widget的child数组中每一个widget,通过Element的inflateWidget(),获取每一个子widget的Element,内部保存_children数组(Element)。


任重而道远

相关文章

网友评论

      本文标题:Flutter之Widget和Element

      本文链接:https://www.haomeiwen.com/subject/qjmqzctx.html