NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

Flutter 的渲染逻辑,以及与 Native 通信(一)

  • 2019-11-30
  • 本文字数:3646 字

    阅读完需:约 12 分钟

Flutter 的渲染逻辑,以及与 Native 通信(一)

本文来自 RTC 开发者社区(rtcdeveloper.com)的用户投稿,作者是一位资深 Android 工程师,熟悉跨平台开发。在这篇文章中,主要包含两个部分内容, Flutter 的渲染逻辑,以及 Flutter 和 Native 互通的方法,这里的 Native 是以 Android 为例。对于在 Flutter 上构建实时音视频场景开发者有一定的参考意义。

1 Flutter 渲染

在 Android 中,我们所说的 View 的渲染逻辑指的是 onMeasure(), onLayout(), onDraw(), 我们只要重写这三个方法就可以自定义出符合我们需求的 View。其实,即使我们不懂 Android 中 View 的渲染逻辑,也能写出大部分的 App,但是当系统提供的 View 满足不了我们的需求的时候,这时就需要我们自定义 View 了,而自定义 View 的前提就是要知道 View 的渲染逻辑。


Flutter 中也一样,系统提供的 Widget 可以满足我们大部分的需求,但是在一些情况下我们还是得渲染自己的 Widget。


和 Android 类似,Flutter 中的渲染也会经历几个必要的阶段,如下:


  • Layout : 布局阶段,Flutter 会确定每一个子 Widget 的大小和他们在屏幕中将要被放置的位置。

  • Paint : 绘制阶段,Flutter 为每个子 Widget 提供一个 canvas,并让他们绘制自己。

  • Composite : 组合阶段,Flutter 会将所有的 Widget 组合在一起,并交由 GPU 处理。


上面三个阶段中,比较重要的就是 Layout 阶段了,因为一切都始于布局。


在 Flutter 中,布局阶段会做两个事情:父控件将 约束(Constraints) 向下传递到子控件;子控件将自己的 布局详情(Layout Details) 向上传递给父控件。如下图:



布局过程如下:


这里我们将父 widget 称为 parent;将子 widget 称为 child


  1. parent 会将某些布局约束传递给 child,这些约束是每个 child 在 layout 阶段必须要遵守的。如同 parent 这样告诉 child :“只要你遵守这些规则,你可以做任何你想做的事”。最常见的就是 parent 会限制 child 的大小,也就是 child 的 maxWidth 或者 maxHeight。

  2. 然后 child 会根据得到的约束生成一个新的约束,并将这个新的约束传递给自己的 child(也就是 child 的 child),这个过程会一直持续到出现没有 child 的 widget 为止。

  3. 之后,child 会根据 parent 传递过来的约束确定自己的布局详情(Layout Details)。如:假设 parent 传递给 child 的最大宽度约束为 500px,child 可能会说:“好吧,那我就用 500px”,或者 “我只会用 100px”。这样,child 就确定了自己的布局详情,并将其传递给 parent。

  4. parent 反过来做同样的事情,它根据 child 传递回来的 Layout Details 来确定其自身的 Layout Details,然后将这些 Layout Details 向上层的 parent 传递,直到到达 root widget (根 widget)或者遇到了某些限制。


那我们上面所提到的 约束(Constraints) 和 布局详情(Layout Details) 都是什么呢?这取决于布局协议(Layout protocol)。Flutter 中有两种主要的布局协议:Box Protocol 和 Sliver Protocol,前者可以理解为类似于盒子模型协议,后者则是和滑动布局相关的协议。这里我们以前者为例。


在 Box Protocol 中,parent 传递给 child 的约束都叫做 BoxConstraints 这些约束决定了每个 child 的 maxWidth 和 maxHeight 以及 minWidth 和 minHeight。如:parent 可能会将如下的 BoxConstraints 传递给 child。



上图中,浅绿色的为 parent,浅红色的小矩形为 child。 那么,parent 传递给 child 的约束就是 150 ≤ width ≤ 300, 100 ≤ height ≤ 无限大 而 child 回传给 parent 的布局详情就是 child 的尺寸(Size)。


有了 child 的 Layout Details ,parent 就可以绘制它们了。


在我们渲染自己的 widget 之前,先来了解下另外一个东西 Render Tree。


Render Tree


我们在 Android 中会有 View tree,Flutter 中与之对应的为 Widget tree,但是 Flutter 中还有另外一种 tree,称为 Render tree。


在 Flutter 中 我们常见的 widget 有 StatefulWidget,StatelessWidget,InheritedWidget 等等。但是这里还有另外一种 widget 称为 RenderObjectWidget,这个 widget 中没有 build() 方法,而是有一个 createRenderObject() 方法,这个方法允许创建一个 RenderObject 并将其添加到 render tree 中。


RenderObject 是渲染过程中非常重要的组件,render tree 中的内容都是 RenderObject,每个 RenderObject 中都有许多用来执行渲染的属性和方法:


  • constraints : 从 parent 传递过来的约束。

  • parentData: 这里面携带的是 parent 渲染 child 的时候所用到的数据。

  • performLayout():此方法用于布局所有的 child。

  • paint():这个方法用于绘制自己或者 child。

  • 等等…


但是,RenderObject 是一个抽象类,他需要被子类继承来进行实际的渲染。RenderObject 的两个非常重要的子类是 RenderBox 和 RenderSliver 。这两个类是所有实现 Box Protocol 和 Sliver Protocol 的渲染对象的父类。而且这两个类还扩展了数十个和其他几个处理特定场景的类,并且实现了渲染过程的细节。


现在我们开始渲染自己的 widget,也就是创建一个 RenderObject。这个 widget 需要满足下面两点要求:


  • 它只会给 child 最小的宽和高

  • 它会把它的 child 放在自己的右下角


如此 “小气” 的 widget ,我们就叫他 Stingy 吧!Stingy 所属的树形结构如下:


MaterialApp  |_Scaffold  |_Container      // Stingy 的 parent    |_Stingy      // 自定义的 RenderObject      |_Container   // Stingy 的 child
复制代码


代码如下:


void main() {  runApp(MaterialApp(    home: Scaffold(      body: Container(        color: Colors.greenAccent,        constraints: BoxConstraints(            maxWidth: double.infinity,            minWidth: 100.0,            maxHeight: 300,            minHeight: 100.0),        child: Stingy(          child: Container(            color: Colors.red,          ),        ),      ),    ),  ));}
复制代码


Stingy


class Stingy extends SingleChildRenderObjectWidget {  Stingy({Widget child}) : super(child: child);
@override RenderObject createRenderObject(BuildContext context) { // TODO: implement createRenderObject return RenderStingy(); }}
复制代码


Stingy 继承了 SingleChildRenderObjectWidget,顾名思义,他只能有一个 child 而 createRenderObject(…) 方法创建并返回了一个 RenderObject 为 RenderStingy 类的实例


RenderStingy


class RenderStingy extends RenderShiftedBox {  RenderStingy() : super(null);  // 绘制方法  @override  void paint(PaintingContext context, Offset offset) {      // TODO: implement paint    super.paint(context, offset);  }     // 布局方法  @override  void performLayout() {       // 布局 child 确定 child 的 size    child.layout(        BoxConstraints(            minHeight: 0.0,            maxHeight: constraints.minHeight,            minWidth: 0.0,            maxWidth: constraints.minWidth),        parentUsesSize: true);        print('constraints: $constraints');             // child 的 Offset    final BoxParentData childParentData = child.parentData;    childParentData.offset = Offset(constraints.maxWidth - child.size.width,        constraints.maxHeight - child.size.height);    print('childParentData: $childParentData');    // 确定自己(Stingy)的大小 类似于 Android View 的 setMeasuredDimension(...)    size = Size(constraints.maxWidth, constraints.maxHeight);    print('size: $size');  }}
复制代码


RenderStingy 继承自 RenderShiftedBox,该类是继承自 RenderBox。RenderShiftedBox 实现了 Box Protocol 所有的细节,并且提供了 performLayout() 方法的实现。我们需要在 performLayout() 方法中布局我们的 child,还可以设置他们的偏移量。


我们在使用 child.layout(…) 方法布局 child 的时候传递了两个参数,第一个为 child 的布局约束,而另外一个参数是 parentUserSize, 该参数如果设置为 false,则意味着 parent 不关心 child 选择的大小,这对布局优化比较有用;因为如果 child 改变了自己的大小,parent 就不必重新 layout 了。但是在我们的例子中,我们的需要把 child 放置在 parent 的右下角,这意味着如果 child 的大小(Size)一旦改变,则其对应的偏移量(Offset) 也会改变,这就意味着 parent 需要重新布局,所以我们这里传递了一个 true。


当 child.layout(…) 完成了以后,child 就确定了自己的 Layout Details。然后我们就还可以为其设置偏移量来将它放置到我们想放的位置。在我们的例子中为 右下角。


最后,和 child 根据 parent 传递过来的约束选择了一个尺寸一样,我们也需要为 Stingy 选择一个尺寸,以至于 Stingy 的 parent 知道如何放置它。类似于在 Android 中我们自定义 View 重写 onMeasure(…) 方法的时候需要调用 setMeasuredDimension(…) 一样。


运行效果如下:



2019-11-30 15:04540

评论

发布
暂无评论
发现更多内容

Parallels Desktop如何退出账号?PD18虚拟机退出账号方法

Rose

pd18虚拟机 PD如何退出账号 Parallels Desktop下载 Parallels破解版 Mac虚拟机下载

DR5白金版 for mac(PS一键磨皮插件Delicious Retouch)支持ps2022 v5.0汉化版

Rose

DR5白金版 PS一键磨皮插件 dr5插件 汉化版PS插件 dr5插件教程

基于Redis讲解,学懂缓存雪崩,缓存击穿,缓存穿透

做梦都在改BUG

Java redis 缓存穿透 缓存击穿 缓存雪崩

阿里“妈宝级”之作,Kubernetes原理剖析与实战应用手册,太全了

做梦都在改BUG

Java Kubernetes k8s

分布式事务的21种武器 - 7

俞凡

架构 云原生

阿里巴巴Java开发手册(华山版)

Java你猿哥

Java MySQL ssm JAVA开发 Alibaba

AE模板:短信消息聊天对话气泡动画Smart Text Message

Rose

AE模板下载 Smart Text Message 对话框气泡生生成器插件

国外顶级架构师编写2580页DDD领域驱动设计笔记,看到内容后破防了

做梦都在改BUG

Java 架构 领域驱动设计 DDD

关于Spring Cloud Alibaba,看阿里这篇笔记真香

Java你猿哥

分布式事务 微服务 Spring Cloud 服务治理 Spring Cloud Aliababa

利用springboot初始化机制三种实现策略模式的应用

Java你猿哥

Java spring Spring Boot 设计模式 ssm

Redis和MySQL的爱恨情仇!

Java你猿哥

Java MySQL redis ssm 缓存雪崩

非常全面的 SpringBoot 保姆级笔记,面面俱到,太牛了

做梦都在改BUG

Java spring 微服务 Spring Boot 框架

阿里巴巴工程师1480道Java面试题及答案整理( 2023年 整理版)

Java你猿哥

Java MySQL redis JVM java面试

mac分屏功能怎么用?mac分屏软件推荐 magnet

魔仙苹果mac堡

Magnet中文版 Magnet破解版 mac分屏功能 Magnet Mac下载 苹果窗口管理软件

软件测试/测试开发丨学习笔记之Web自动化测试

测试人

程序员 软件测试 自动化测试 测试开发 web自动化

ps神经滤镜是干什么的,神经滤镜的功能和作用

魔仙苹果mac堡

ps神经滤镜 PS2023破解 Neural Filters下载 Photoshop2023 Mac

耗时一周整理的牛客网上最火Java面试八股文,面试必备

架构师之道

Java 面试

StarUML教程:CLI(命令行界面)功能的使用

魔仙苹果mac堡

StarUML教程 CLI(命令行界面) UML软件建模器 StarUML for Mac StarUML Mac破解下载

太牛了!在GitHub上“千金难求”的SpringBoot趣味实战课免费分享

Java你猿哥

Java spring Spring Boot ssm SpringBoot实战

photoshop神经滤镜是什么,神经滤镜功能揭秘~

Rose

photoshop神经滤镜 Neural Filters插件 ps滤镜下载 PS2023破解

Java面试很难?啃完阿里老哥这套Java架构速成笔记,我都能拿30K

做梦都在改BUG

五十万字总结!2023最新Java面试八股汇总(含答案,收藏版)

采菊东篱下

Java 面试

学懂缓存雪崩,缓存击穿,缓存穿透仅需一篇,基于Redis讲解

Java你猿哥

Java redis 缓存击穿 缓存雪崩 Redis缓存穿透

GitHub星标126K的京东「微服务进阶笔记」首次开源!好评如潮

Java你猿哥

Java 架构 微服务 Spring Cloud ssm

简直人生外挂,直接涨薪25K,跪谢这份Java性能调优实战宝典

做梦都在改BUG

Java 性能优化 性能调优

Parallels Desktop如何退出账号?PD18虚拟机退出账号方法

魔仙苹果mac堡

Parallels Desktop下载 PD18虚拟机破解 Parallels如何退出账号

Java面试集锦

源字节1号

微信小程序 开源 软件开发 后端开发

爆肝了!阿里最新版的这份Spring Security源码手册,狂揽GitHub榜首

做梦都在改BUG

Java spring spring security Spring Security OAuth

软件测试/测试开发丨Web自动化 PageObject 设计模式

测试人

程序员 软件测试 自动化测试 测试开发 Web自动化测试

工厂+策略在springboot项目中的使用场景

做梦都在改BUG

Java Spring Boot 工厂模式 策略模式

Typora for Mac:一款极简风格Markdown写作软件

魔仙苹果mac堡

Typora破解 Mac软件 Markdown文本编辑器 Typora Mac下载

Flutter 的渲染逻辑,以及与 Native 通信(一)_文化 & 方法_声网_InfoQ精选文章