Fultter之Element和Widget对应关系解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chunqiuwei/article/details/88065851

在StatelessWidget StatefullWidget与Element的关系 博文中简单的说明了二者与Element的逻辑关系。
通过该片博客可以知道Widget提供了一个createElment的抽象方法:

class Widget {
  Element createElement();
}

到此,本篇博客的设计的前提知识已经具备,下面具体分析下Widget的关系。

下面就Widget与Element的关系在做进一步的分析和巩固。

在Widget中有如下几种常见的Widget,其继承关系如图所示:
在这里插入图片描述

那么再来看看Element的图示:
在这里插入图片描述
乍一看上下两幅图是一样的,其实因为Widget提供了createElement这个抽象方法,本身就说明每个不同的Widget都有对应的Element对象。

在这里着重说一下RenderObjectWidget,看下表:

Widget 说明 举例
MultiChildRenderObjectWidget 该类型的Widget可以添加多个widget Row,Column,Stack
SingleChildRenderObjectWIdget 该类型的widget只能添加一个widget Center,Padding,Container
LeafRenderObjectWidget 该类型是树的叶子节点,故不能添加widget Text,Image,Semantics

了解了上面的基本信息后,看下面的代码进行分析:

Widget _createWidget(){
    return Center(
      child: Container(
        child: Row(
          children: <Widget>[
            Text("A"),
            Text("B")
          ],
        ),
      ),
    );
  }

上面createWidget方法组成的Widget树状图以及对应的Element的树状图如下所示:

在这里插入图片描述
从上面的图中我们可以清楚的看到Widget和Element的对应关系。那么Widget的createElement是神马时候初始化的呢?根据Flutter官方文档对Element的注释说明,我们需要查看Element的mount方法。经过博主研究建议读者阅读MultiChildRenderObjectWidget对象对应的MultiChildRenderObjectElement,直接查看MultiChildRenderObjectElement的mout方法即可:

  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;
    }
  }

在mount方法中可以看出循环遍历了MultiChildRenderObjectWidget的children集合,为集合中的每一个child widget调用inflateWidget方法,该方法返回了child widget对应的Element对象。注意inflateWidget方法由两个参数,一个是children集合中的某个Widget,一个是Element对象。

所以接下来看看inflateWidget方法都做了神马:

  Element inflateWidget(Widget newWidget, dynamic newSlot) {
    //省略部分更新Widget的逻辑。

  //创建childWidget的具体Element对象
    final Element newChild = newWidget.createElement();
   
   //继续调用childElement的mount方法。
    newChild.mount(this, newSlot);

    return newChild;
  }

排除了与本文无关的相关代码之后,inflateWidget代码逻辑很简单:
1、调用widget的createElement方法来创建Widget对应的Element对象(终于知道Widget的createElement方法什么时候调用的了吧)
2、调用步骤1创建的Element的mount方法,如果新创建的Elment是MultiChildRenderObjectElement的话,则会继续调用该MultiChildRenderObjectElement的mount,并循环其children循环执行inflateWidget方法,也就是重复步骤1.


下面在用一个例子代码来讲解其具体过程,该过程可以回答在StatelessWidget StatefullWidget与Element的关系 这篇博客遗留的问题:

      Row(
          children: <Widget>[
            Text("A"),
            Text("B")
          ],
        )

比如上面的这段代码,因为Row这个组件父类是MultiChildRenderObjectWidget,其对应的Elmenent是MultiChildRenderObjectElement.其中Row包含了两个child Widget 即两个Text。那么根据上面的mount分析就需要循环遍历两个Text,先调用Text的createElement方法创建其Element方法。因为Text extends StatelessWidget.所以Text.createElement返回的就是StatelessElement.

紧接着StatelessElement对象又调用了mount方法,具体的是在StatelessElment的父类ComponentElement的mount方法:

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

如果读者读过博主的博客在StatelessWidget StatefullWidget与Element的关系 这篇博文,就知道了StatelessWidget的build方法是在_firstBuild方法里面执行的了。简单的执行逻辑如下:
在这里插入图片描述
到此为止本篇博文就结束了,如有不当之处欢迎批评指正,共同学习

猜你喜欢

转载自blog.csdn.net/chunqiuwei/article/details/88065851