美文网首页
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