美文网首页Flutter
Flutter学习之三 Widget

Flutter学习之三 Widget

作者: MQ_Twist | 来源:发表于2022-03-31 13:52 被阅读0次

    骐骥一跃,不能十步;驽马十驾,功在不舍。

    👈🏻 Flutter学习之二 第一个App

    前言

    不管有没有的写过或者学习过flutter的coder,多少都听过flutter的一切皆组件。是的,没错一切皆组件,flutter中几乎所有的对象都是一个Widget。记住!Flutter 中万物皆为Widget!

    正文

    一、概念

    widget它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector 、用于APP主题数据传递的 Theme 等等,而原生开发中的控件通常只是指UI元素。在后面的内容中,我们在描述UI元素时可能会用到“控件”、“组件”这样的概念,读者心里需要知道他们就是 widget ,只是在不同场景的不同表述而已。由于 Flutter 主要就是用于构建用户界面的,所以,在大多数时候,读者可以认为 widget 就是一个控件,不必纠结于概念。

    二、Widget API简介

    在 Flutter 中, widget 的功能是“描述一个UI元素的配置信息”,它就是说, Widget 其实并不是表示最终绘制在设备屏幕上的显示元素,所谓的配置信息就是 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;
      }
    
      static int _debugConcreteSubtype(Widget widget) {
        return widget is StatefulWidget ? 1 :
               widget is StatelessWidget ? 2 :
               0;
      }
    }
    
    • @immutable 代表 Widget 是不可变的,这会限制 Widget 中定义的属性(即配置信息)必须是不可变的(final),为什么不允许 Widget 中定义的属性变化呢?这是因为,Flutter 中如果属性发生则会重新构建Widget树,即重新创建新的 Widget 实例来替换旧的 Widget 实例,所以允许 Widget 的属性变化是没有意义的,因为一旦 Widget 自己的属性变了自己就会被替换。这也是为什么 Widget 中定义的属性必须是final的原因。
    • widget类继承自DiagnosticableTreeDiagnosticableTree即“诊断树”,主要作用是提供调试信息。
    • Key: 这个key属性类似于 React/Vue 中的key,主要的作用是决定是否在下一次build时复用旧的 widget ,决定的条件在canUpdate()方法中。
    • createElement():正如前文所述“一个 widget 可以对应多个Element”;Flutter 框架在构建UI树时,会先调用此方法生成对应节点的Element对象。此方法是 Flutter 框架隐式调用的,在我们开发过程中基本不会调用到。
    • debugFillProperties(...) 复写父类的方法,主要是设置诊断树的一些特性。
    • canUpdate(...)是一个静态方法,它主要用于在 widget 树重新build时复用旧的 widget ,其实具体来说,应该是:是否用新的 widget 对象去更新旧UI树上所对应的Element对象的配置;通过其源码我们可以看到,只要newWidgetoldWidgetruntimeTypekey同时相等时就会用new widget去更新Element对象的配置,否则就会创建新的Element

    三、Flutter中的四颗树

    既然 Widget 只是描述一个UI元素的配置信息,那么真正的布局、绘制是由谁来完成的呢?Flutter 框架的的处理流程是这样的:

    1、根据 Widget 树生成一个 Element 树,Element 树中的节点都继承自Element类。
    2、根据 Element 树生成 Render 树(渲染树),渲染树中的节点都继承自RenderObject 类。
    3、根据渲染树生成 Layer 树,然后上屏显示,Layer 树中的节点都继承自 Layer 类。

    真正的布局和渲染逻辑在 Render 树中,ElementWidgetRenderObject 的粘合剂,可以理解为一个中间代理。我们通过一个例子来说明,假设有如下 Widget 树:

    Container( // 一个容器 widget
      color: Colors.blue, // 设置容器背景色
      child: Row( // 可以将子widget沿水平方向排列
        children: [
          Image.network('https://www.example.com/1.png'), // 显示图片的 widget
          const Text('A'),
        ],
      ),
    );
    

    注意,如果 Container 设置了背景色,Container 内部会创建一个新的 ColoredBox 来填充背景,相关逻辑如下:

    if (color != null)
      current = ColoredBox(color: color!, child: current);
    

    而 Image 内部会通过 RawImage 来渲染图片、Text 内部会通过 RichText 来渲染文本,所以最终的 Widget树、Element 树、渲染树结构如图1所示:

    1.png

    这里需要注意:

    1. 三棵树中,Widget 和 Element 是一一对应的,但并不和 RenderObject 一一对应。比如 StatelessWidgetStatefulWidget 都没有对应的 RenderObject。
    2. 渲染树在上屏前会生成一棵 Layer 树,这个比较难理解,可以先记着有这么个东西,我学会了再写QAQ。

    👉🏻 Flutter学习之四 StatelessWidget 和 StatefulWidget

    相关文章

      网友评论

        本文标题:Flutter学习之三 Widget

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