likes
comments
collection
share

Flutter 中的 Widget, Element, RenderObject(二)

作者站长头像
站长
· 阅读数 27

Flutter 中的 Widget, Element, RenderObject(二)

class Padding extends SingleChildRenderObjectWidget {
  // ...
  @override
  RenderPadding createRenderObject(BuildContext context) {
    return RenderPadding(
      // ...
    );
  }
}

class RichText extends MultiChildRenderObjectWidget {
  // ...
  @override
  RenderParagraph createRenderObject(BuildContext context) {
    return RenderParagraph(
      // ...
    );
  }
  // ...
}

Widget

Flutter 中一切皆 Widget,我们先给 Widget 分一下类。

  • 最常用的两个: StatelessWidget, StatefulWidget,它们没有自己的 RenderObject,用于组合其他 Widget
  • 具有 RenderObject: RichText, Padding, Opacity
  • Flutter 预设的: Text, Container 等,使用非常频繁,Flutter 预先封装好的 StatelessWidget
  • Flutter 框架层面的: RenderObjectWidget, Leaf/SingleChild/MultiChildRenderObjectWidget

他们的继承关系如下图。

Flutter 中的 Widget, Element, RenderObject(二)

我们看 Widget 接口的源码。

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
const Widget({ this.key });

final Key? key;

@protected
  @factory
  Element createElement();
  
static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}

Widget 是个抽象类,其声明的方法 createElement 需要子类是去实现,这就体现了我们前面说的通过 widget 树创建 Element 树,这两棵树是一一对应的关系。显然,在 StatelessWidget, StatefulWidget, Leaf/SingleChild/MultiChildRenderObjectWidget 里面可以找到 createElement 的实现。

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

abstract class StatefulWidget extends Widget {
@override
  StatefulElement createElement() => StatefulElement(this);

@protected
  @factory
  State createState();
}

abstract class LeafRenderObjectWidget extends RenderObjectWidget {
  @override
  LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}

abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {  
  @override
  SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
}

abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
  @override
  MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
}

可以发现,在创建 Element 时都会传入 this 进去,也就是当前的 widget,然后再返回对应的 Element,这些 Element 都继承自 Element 类,Elment 拥有当前 widget 的引用。

我们之前说过:

Element 挂载时创建 RenderObject

RenderObject 的创建与更新的方法其实是在具体的 widget 中声明的。

class Padding extends SingleChildRenderObjectWidget {
  // ...
  @override
  RenderPadding createRenderObject(BuildContext context) {
    return RenderPadding(
      // ...
    );
  }
  @override
  void updateRenderObject(BuildContext context, RenderPadding renderObject) {
    renderObject
      ..padding = padding
      ..textDirection = Directionality.maybeOf(context);
  }
}

所以,Element 需要拿到 widget 的引用,这样 Element 在创建和更新 RenderObject 时,去调用 widget 中声明的这两个方法。

@override
void mount(Element? parent, Object? newSlot) {
  super.mount(parent, newSlot);
  // ...
  _renderObject = (widget as RenderObjectWidget).createRenderObject(this);
  // ...
  attachRenderObject(newSlot);
  super.performRebuild(); // clears the "dirty" flag
}

Flutter 这么设计的原因是,我们平时写的 widget 只是配置信息,它需要的是 RenderObject (而不是Element)便于将配置信息直接传入进行渲染。 这就弄清楚了文章开头说的好像是 widget 创建的 RenderObject 的疑惑。

Element

上面源码中,我们介绍了 Widget 都会生成对应的 Element 树,与 Widget 一样,我们也对 Element 做一下分类。

Flutter 中的 Widget, Element, RenderObject(二)

我们来看看 Element 接口。

abstract class Element extends DiagnosticableTree implements BuildContext {
  /// Creates an element that uses the given widget as its configuration.
 ///
 /// Typically called by an override of [Widget.createElement].
Element(Widget widget)
    : _widget = widget {
    // ...
  }
  // ...
}

前面说到 createElement 方法出入的 this

@override
SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);

就是一级一级透传至 Element 构造方法中的。

class SingleChildRenderObjectElement extends RenderObjectElement {
  /// Creates an element that uses the given widget as its configuration.
SingleChildRenderObjectElement(SingleChildRenderObjectWidget super .widget);
}

abstract class RenderObjectElement extends Element {
  /// Creates an element that uses the given widget as its configuration.
RenderObjectElement(RenderObjectWidget super .widget);
}

this 最终赋值给了 Element_widget 变量。

@override
Widget get widget => _widget!;
Widget? _widget;

从源码中可以看到,Element 里面的 widget 是个 get 方法,直接返回 _widget。从源码中的注释,也再次看到 WidgetElement 的关系,WidgetElement 的配置。

对于 Element 的构造方法,StatefulElement 有些特殊的地方。

class StatelessElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
StatelessElement(StatelessWidget super.widget);
}

class StatefulElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
StatefulElement(StatefulWidget widget)
      : _state = widget.createState() ,
        super(widget) {
          // ...
          state._element = this;
          // ...
          state._widget = widget;
        }
        
  /// The [State] instance associated with this location in the tree.
 ///
 /// There is a one-to-one relationship between [State] objects and the
 /// [StatefulElement] objects that hold them. The [State] objects are created
 /// by [StatefulElement] in [mount].
  State<StatefulWidget> get state => _state!;
  State<StatefulWidget>? _state;
}

StatefulElement 的构造方法还调用了对应 widgetcreateState 方法,并将其赋值给 _state 变量。我们平时写的 StatefulWidget 中的 createState 方法就是在这个时机调用的。

StatefulElement 不仅有对 widget 的引用,也有对 StatefulWidgetState 的引用。在构造函数中也将传进去 widget 赋值给 _state_widget 属性。所以我们在 State 中可以使用 widget. 的方式拿到 widget 中的值。

appBar: AppBar(
  backgroundColor: Theme.of(context).colorScheme.inversePrimary,
  title: Text(widget.title),
),

前面文章中有说到渲染过程:

  • 首先,通过 Widget 树生成对应的 Element 树;
  • 然后,Element 挂载时创建 RenderObject,并关联至 Element.renderObject 属性上;
  • 最后,RenderObject 树完成最终的渲染。

createElementmount 都是 Flutter 框架自动调用的,开发者不必关注这些过程。基类 Element 里面的 mount 方法需要子类来实现。我们先来看看 ComponentElement 中的 mount 方法。

// ComponentElement
@override
void mount(Element? parent, Object? newSlot) {
  // ...
  _firstBuild();
}

经过深挖发现,这里执行的顺序如下:

Flutter 中的 Widget, Element, RenderObject(二)

我们来看看 StatelessElement/StatefulElement 中的 build 方法。

// StatelessElement
@override
Widget build() => (widget as StatelessWidget).build(this);

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

对比很明显,StatelessElement 执行的是 widget 中的 build 方法,而 StatefulElement 执行的是 Statebuild 方法。二者的 build 中都是传入的 this,也就是当前的 Element,我们在写代码时,build 方法传入的都是 context

@override
Widget build(BuildContext context) {}

所以 BuildContext 其实就是当前 widget 所对应的 Element。事实上,Element 就是实现了 BuildContext

abstract class Element extends DiagnosticableTree implements BuildContext {}

我们再来看看 RenderObjectElement 中的 mount 方法。

// RenderObjectElement
@override
void mount(Element? parent, Object? newSlot) {
  super.mount(parent, newSlot);
  // ...
  _renderObject = (widget as RenderObjectWidget).createRenderObject(this);
  // ...
  attachRenderObject(newSlot);
  super.performRebuild(); // clears the "dirty" flag
}

对比 ComponentElement(StatelessElement, StatefulElement)里面的 mount 方法,RenderObjectElementmount 方法是调用 widgetcreateRenderObject 方法生成对应的 RenderObject,然后赋值给 _renderObject

说到这里,我们再来回顾一下前面对 Widget 的分类。

  • 最常用的两个: StatelessWidget, StatefulWidget,它们没有自己的 RenderObject,用于组合其他 Widget
  • 具有 RenderObject: RichText, Padding, Opacity
  • Flutter 预设的: Text, Container 等,使用非常频繁,Flutter 预先封装的 StatelessWidget
  • Flutter 框架层面的: RenderObjectWidget, Leaf/SingleChild/MultiChildRenderObjectWidget

StatelessWidget, StatefulWidget 没有自己��� RenderObject,他们只是用来组合其他 widget 的。Flutter 中的页面搭建就像是搭积木,通过不同的组合可以搭建出不一样的页面。而像 Padding, Opacity 等就是积木,这些积木都有自己的 RenderObject

总结来说,ComponentElement 中的 mount 方法是执行 widgetbuild 方法,就是我们自己写的代码(继承自 StatelessWidgetStatefulWidget ),而 RenderObjectElement 中的 mount 方法作用是调用“积木”的 createObject 方法生成对应的 RenderObject,用于页面渲染。

RenderObject

关于 RenderObject,其实前面都有交叉介绍到了,这里再做些补充和总结。

  • 负责实现视图渲染的对象,是个总称,不同的 widget 会有不同类型的 RenderObject
  • 并不是所有的 widget 都有 RenderObject,只有继承自 RenderObjectWidgetWidget 才有对应的 RenderObject
  • RenderObject 的创建与更新的方法是在具体的 widget 里面定义的
  • RenderObjectElement 执行 mount 方法的时候调用的 widget 里面的 createRenderObject 方法的
  • RenderObjectElement 里面既有对 widget 的引用也有对 RenderObject 的引用。

RenderObjectFlutter 的展示过程分为四个阶段,即布局、绘制、合成和渲染。 其中,布局和绘制在 RenderObject 中完成,Flutter 采用深度优先机制遍历 RenderObject 树,确定树中各个对象的位置和尺寸,并把它们绘制到不同的图层上。绘制完毕后,合成和渲染的工作则交给 Skia 来搞定。在 VSync 信号同步时直接从渲染树合成 Bitmap,然后提交给 GPU

转载自:https://juejin.cn/post/7393189744330276902
评论
请登录