Flutter 源码梳理系列(七):RenderObjectWidget、Leaf、Single、MultiChildRenderObjectWidget

零 Flutter教程评论112字数 7614阅读25分22秒阅读模式

Flutter 源码梳理系列(七):RenderObjectWidget、Leaf、Single、MultiChildRenderObjectWidget

RenderObjectWidget 为 RenderObjectElement 提供配置,而 RenderObjectElement 包装 RenderObject,后者负责实际渲染应用程序。简单来说,RenderObjectWidget 是用来配置如何渲染应用程序的规则,RenderObject 则是负责实际渲染应用程序内容的工具。就像是 RenderObjectWidget 负责告诉 RenderObjectElement 如何完成渲染,而 RenderObjectElement 则告诉 RenderObject 具体怎么做。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

通常情况下,如果想要创建一个自定义的渲染对象(RenderObjectWidget),我们不直接继承 RenderObjectWidget,而是选择继承下面这些类之一,(它们都是 RenderObjectWidget 的直接子类。):文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

  • LeafRenderObjectWidget: 如果 widget 没有子项,则使用。
  • SingleChildRenderObjectElement: 如果 widget 恰好有一个子项,则使用。
  • MultiChildRenderObjectWidget: 如果 widget 接受子项列表,则使用。
  • SlottedMultiChildRenderObjectWidget: 如果小部件将其子项组织在不同命名 slot 中,则使用。

RenderObjectWidget 子类必须实现:createRenderObject 和 updateRenderObject 函数。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

RenderObjectWidget

RenderObjectWidget 一个直接继承自 Widget 的抽象类,常量构造函数,还是那个样子,没什么特别的。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

dart

复制代码
abstract class RenderObjectWidget extends Widget {
  // 抽象 const 构造函数。此构造函数使子类能够提供常量构造函数,以便它们可以在常量表达式中使用。
  const RenderObjectWidget({ super.key });
  // ...
}

createElement

@factory 注解,表示不同的 RenderObjectWidget 子类都要实现这个抽象工厂方法,不同的 RenderObjectWidget 子类创建了不同的 RenderObjectElement 子类。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

RenderObjectWidget 对象总是会 inflate 为一个 RenderObjectElement 子类。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

dart

复制代码
  @override
  @factory
  RenderObjectElement createElement();

到目前为止,我们看到的 Widget 子类都实现了自己的 createElement 函数,都分别创建自己对应类型的 Element 对象。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

  • Widget => StatelessWidget -> StatelessElement
  • Widget => StatefulWidget -> StatefulElement
  • Widget => ProxyWidget => ParentDataWidget<T extends ParentData> -> ParentDataElement<T>
  • Widget => ProxyWidget => InheritedWidget -> InheritedElement
  • Widget => RenderObjectWidget => LeafRenderObjectWidget -> LeafRenderObjectElement
  • Widget => RenderObjectWidget => SingleChildRenderObjectWidget -> SingleChildRenderObjectElement
  • Widget => RenderObjectWidget => MultiChildRenderObjectWidget -> MultiChildRenderObjectElement
  • Widget => RenderObjectWidget => SlottedMultiChildRenderObjectWidget -> SlottedMultiChildRenderObjectElement

createRenderObject

RenderObjectWidget 的抽象方法,需要 RenderObjectWidget 子类去实现。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

使用此 RenderObjectWidget 描述的配置创建此 RenderObjectWidget 表示的 RenderObject 类的一个实例。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

此方法不应该对 render object 的子项进行任何操作。相反,应该由重写 RenderObjectElement.mount 方法的方法处理,该方法在被该对象的 createElement 方法创建的 Element 对象挂载到 Element Tree 中时进行调用。例如,参见 SingleChildRenderObjectElement.mount。文章源自灵鲨社区-https://www.0s52.com/bcjc/flutterjc/16502.html

dart

复制代码
  @protected
  @factory
  RenderObject createRenderObject(BuildContext context);

可以和其它 Widget 子类对比着记忆:如所有的 Widget 子类都有 createElement 方法,当遇到 StatefulWidget 时它的子类多了一个:createState 方法,而到了 RenderObjectWidget 时它的子类多了一个:createRenderObject 方法。

全局搜索发现仅在 RenderObjectElement 的 mount 函数内部对 createRenderObject 函数的调用:_renderObject = (widget as RenderObjectWidget).createRenderObject(this),即当 RenderObjectElement 节点挂载到 Element tree 上时会同步创建 RenderObject 对象并赋值给自己的 _renderObject 字段。(即:RenderObjectElement 对象会引用 RenderObject 对象。)

updateRenderObject

一个 RenderObjectWidget 的非抽象方法,但是实现内容为空,会由 RenderObjectWidget 的各个子类选择重写。

将此 RenderObjectWidget 描述的配置复制到给定的 RenderObject 中,该 RenderObject 将与此对象的 createRenderObject 方法返回的相同类型。

此方法不应对更新 render object 的子项执行任何操作。相反,应该由重写了此对象的 createElement 方法中渲染的对象上的 RenderObjectElement.update 方法处理。例如,请参阅 SingleChildRenderObjectElement.update。

dart

复制代码
  @protected
  void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }

didUnmountRenderObject

一个 RenderObjectWidget 的非抽象方法,但是实现内容为空,由 RenderObjectWidget 的各个子类选择重写。

此 widget 先前关联的 render object 已从树中移除。给定的 RenderObject 将与此对象的 createRenderObject 返回的相同类型。

dart

复制代码
  @protected
  void didUnmountRenderObject(covariant RenderObject renderObject) { }

到这里,RenderObjectWidget 的内容就看完了,它最突出的特点,就是:createRenderObject 抽象函数,需要它的各子类实现。看到这里可以验证:只有 RenderObjectWidget 才可以创建 RenderObject。其它的更新 RenderObject 和移除 RenderObject ,我们后面再看。

然后就是它的三个最主要的直接子类了:

  • LeafRenderObjectWidget、
  • SingleChildRenderObjectWidget、
  • MultiChildRenderObjectWidget,

看名字可以看出,它们是根据三者的 子级 情况来进行区分的,它们三个的内容并不多,我们快速过一下。

LeafRenderObjectWidget

一个没有子级 Widget 的 RenderObjectWidget 的直接抽象子类。子类必须实现 createRenderObject 和 updateRenderObject。

dart

复制代码
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
  // 抽象 const 构造函数。该构造函数使得子类能够提供 const 构造函数,以便在 const 表达式中使用。
  const LeafRenderObjectWidget({ super.key });

  @override
  LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}

SingleChildRenderObject

一个只有一个子级 Widget 的 RenderObjectWidget 的直接抽象子类。子类必须实现 createRenderObject 和 updateRenderObject。

分配给此 Widget 的 render object 应该利用 RenderObjectWithChildMixin 实现一个 single-child 模型。该 mixin 公开了 RenderObjectWithChildMixin.child 属性,允许检索属于 Widget 的 render object。

dart

复制代码
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
  // 抽象 const 构造函数。该构造函数使得子类能够提供 const 构造函数,以便在 const 表达式中使用。
  const SingleChildRenderObjectWidget({ super.key, this.child });
  
  // ...

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

child

Widget tree 中位于此 Widget 下方的 Widget。

此 Widget 只能有一个子级 Widget。要布局多个子级 Widget,请让此 Widget 的 child widget 是 Row、Column 或 Stack 等 widget,这些 widget 具有 children 属性,然后将 child widget 提供给该 Widget。

dart

复制代码
  final Widget? child;

同 LeafRenderObjectWidget 比多了:child 字段。

MultiChildRenderObjectWidget

这是一个为 RenderObject 子类配置 RenderObjectWidget 的超类,这些子类有一个子级 Widget 列表(children)。 (这个超类只提供了该 children 的存储空间,实际上并没有提供更新逻辑。)

子类必须使用混合了 ContainerRenderObjectMixin 的 RenderObject,这个 mixin 提供了访问容器渲染对象的子代(即子级 Widget 所属的渲染对象)所需的功能。通常,子类将使用一个既混入 ContainerRenderObjectMixin 又混入 RenderBoxContainerDefaultsMixin 的 RenderBox。

子类必须实现 createRenderObject 和 updateRenderObject。

可参见:

  • Stack,它使用 MultiChildRenderObjectWidget。
  • RenderStack,一个关联 render object 示例实现。
  • SlottedMultiChildRenderObjectWidget,它配置了一个 RenderObject,该 RenderObject 不是使用单个 children 列表,而是将其 child 组织在命名 slot 中。

dart

复制代码
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
  /// Initializes fields for subclasses.
  const MultiChildRenderObjectWidget({ super.key, this.children = const <Widget>[] });
  
  // ...
  
  @override
  MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
}

children

Widget tree 中此 Widget 下方的子级 widget 列表。

如果此列表将被修改,通常明智的做法是在每个子级 Widget 上放置一个 Key,这样 framwwork 可以将旧配置匹配到新配置,从而维护底层的 render object。

另外,在 Flutter 中,Widget 是不可变的,因此直接修改子项如 someMultiChildRenderObjectWidget.children.add(...) 或下面的示例代码将导致不正确的行为。每当修改 children 时,应提供一个新的列表对象。

下面是错误示范代码:

dart

复制代码
// This code is incorrect.
class SomeWidgetState extends State<SomeWidget> {
  final List<Widget> _children = <Widget>[];

  void someHandler() {
    setState(() {
      _children.add(const ChildWidget());
    });
  }

  @override
  Widget build(BuildContext context) {
    // Reusing `List<Widget> _children` here is problematic.
    return Row(children: _children);
  }
}

下面的代码纠正了上面提到的问题。

dart

复制代码
class SomeWidgetState extends State<SomeWidget> {
  final List<Widget> _children = <Widget>[];

  void someHandler() {
    setState(() {
      // 这里的 key 是允许 Flutter 在 children 被重新创建时重复使用底层的 render object。(这里根据 key 可以复用 widget 对应的 element 和 render object)
      _children.add(ChildWidget(key: UniqueKey()));
    });
  }

  @override
  Widget build(BuildContext context) {
    // 始终创建一个新的 children,因为 Widget 是不可变的。
    return Row(children: _children.toList());
  }
}

同上面 SingleChildRenderObjectWidget 比,这里是一个 Widget 列表。

dart

复制代码
  final List<Widget> children;

RenderObjectWidget 总结

至此 LeafRenderObjectWidget、SingleChildRenderObjectWidget、MultiChildRenderObjectWidget 三个 RenderObjectWidget 的抽象子类 的内容粗略看完了,官方文档里面也没有过多的介绍它们,我们自己看的话主要是基于 RenderObjectWidget 的抽象函数:createRenderObject,也即是再往后面的 RenderObjectWidget 子类必须要实现这个创建 Render object 的任务。看到这里可验证:Widget 和 Element 是一一对应的,不同的 Widget 都会实现自己的 createElement 函数,而只有 RenderObjectWidget 才需要创建自己的 render object。

看到这里的话,基本的抽象 Widget 基类,我们就看完了,它们的继承链如下:

  • Object => DiagnosticableTree => Widget => StatelessWidget
  • Object => DiagnosticableTree => Widget => StatefulWidget
  • Object => DiagnosticableTree => Widget => ProxyWidget => ParentDataWidget
  • Object => DiagnosticableTree => Widget => ProxyWidget => InheritedWidget
  • Object => DiagnosticableTree => Widget => RenderObjectWidget => LeafRenderObjectWidget
  • Object => DiagnosticableTree => Widget => RenderObjectWidget => SingleChildRenderObjectWidget
  • Object => DiagnosticableTree => Widget => RenderObjectWidget => MultiChildRenderObjectWidget
  • Object => DiagnosticableTree => Widget => RenderObjectWidget => SlottedMultiChildRenderObjectWidget

粗略总结一下的话:StatefulWidget 子类需要创建自己的 State,ParentDataWidget 子类需要把 parent data 应用到 Render Object 上,InheritedWidget 子类可以把数据传递到远端,并且在更新时通知依赖者进行重建。其它的 RenderObjectWidget 子类则是各自构建自己的 Render Object。

OK,下面我们继续,向 Element 和 RenderObject 发起冲锋。

零
  • 转载请务必保留本文链接:https://www.0s52.com/bcjc/flutterjc/16502.html
    本社区资源仅供用于学习和交流,请勿用于商业用途
    未经允许不得进行转载/复制/分享

发表评论