Flutter 入门 - Widget、Element和 RenderObject 的起源
可能是因为这些文章的切入点不同(或者是我的愚蠢),当我第一次学习 Flutter 时(甚至在我开始使用它一段时间后) ),无法完全理解这三棵树。他的角色是什么?大概最大的好处就是和面试官打交道……
经过一段时间的学习,我将自己对这三类(最基本的职责)的理解总结如下。希望对您深入了解Widget、Element、RenderObject有所帮助。
Widget、Element和RenderObject
纯属个人愚见,如有错误还请指出,谢谢。
复制代码
Widget
在古代,如果古人想要通过 Flutter 创建一个(100*100)的蓝色正方形,必须执行以下操作:
来自 :https://juejin.cn/post/6844904104452440072
作者:恋猫de小郭
import 'dart:ui' as ui;
void main() {
ui.window.onBeginFrame = beginFrame;
ui.window.scheduleFrame();
}
void beginFrame(Duration timeStamp) {
final double devicePixelRatio = ui.window.devicePixelRatio;
///创建一个画板、录制绘制指令
final ui.PictureRecorder recorder = ui.PictureRecorder();
///基于画板创建一个 Canvas
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.scale(devicePixelRatio, devicePixelRatio);
//布局
var centerX = ui.window.physicalSize.width / 2.0;
var centerY = ui.window.physicalSize.height / 2.0;
///画一个 100 的剧中蓝色
Paint blueP = new Paint()..color = Colors.blue;
canvas.drawLine(Offset.zero,Offset(100,0),blueP);
canvas.drawLine(Offset(100,0),Offset(100,100),blueP);
canvas.drawLine(Offset(100,100),Offset(0,100),blueP);
///停止录制绘制指令
final ui.Picture picture = recorder.endRecording();
//布局
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder()
..pushOffset(centerX, centerY)
..addPicture(ui.Offset.zero, picture)
..pop();
ui.window.render(sceneBuilder.build());
}
复制代码
之后经过以上的操作序列,‘蓝色方块’诞生了:
经过长时间的使用,绘制方块的四行代码被优化为:
//很明显这个要比上面那4行更易读、且简便。
//也算是初代之一的‘轮子’了。
canvas.drawRect(
Rect.fromCenter(
center: Offset.zero,
width: 100,
height: 100),
blueP);
复制代码
经过很长一段时间,随着轮子的不断迭代,终于演变为以下内容:
Container(
color:Colors.blue
width:100,height:100)
复制代码
这就是我们Widget的起源。
Element
借助小部件(滚轮)的便利,我只用了一天时间就创建了一个漂亮的页面:
还可以交互:
点击 圆的时候,两个圆颜色互换
点击 方块时,黄色变成了白色
点击 三角时,三角就消失了,再点击空白位置会复现。
复制代码
为了实现这个功能,我创建了一个漂亮的页面在 widget 中添加了很多标志(flags)来表示哪些需要更新、删除、插入等,并且添加了函数来实现相应的功能。
一切都完成后,我的代码一团糟,难以阅读——我花了10分钟写代码,又花了5分钟滚动页面……
因此element诞生了——它是主要负责增删改查widget,哪些不需要更新,哪些需要重建等等,我们看它的方法就一目了然了。
....省略代码
void update(covariant Widget newWidget)
Element inflateWidget(Widget newWidget, dynamic newSlot)
void markNeedsBuild()
void performRebuild();
Element updateChild(Element child, Widget newWidget, dynamic newSlot)
....省略代码
复制代码
RenderObject
元素的添加使我的页面代码更加简洁。同时,我的页面还添加了新的炫酷功能:
双击页面后,会出现一个灰色矩形,盖住原来的图形,并且左右晃动。
复制代码
功能写完后,测试机运行起来,我的主机直接蓝屏。
这是不可能的。向老板要钱是没有希望的。为了省钱,他还是用算盘算账……只能优化代码。
经过观察,发现几个可以优化的地方:
1, 之前的‘方块’和‘三角’被遮挡了,那么再画它们也没啥意义,布局也可以省了
2, 上面两个圆,就没动过,所以布局啊、绘制啥的都没必要再跑一遍
3, 灰色方块,大小没变,颜色也没变,我觉得只要调整它的位置就行
复制代码
先添加7、8勺Flag,再添加10勺左右的功能...
...忙了一段时间工作后,在旧机器上。功能完美。
但是我的代码又臃肿了,所以我只是提取了另一个类,就像之前的元素一样,来帮助我管理绘图、布局、命令捕获(实际上封装在PaintContext中)等等:
‘于是,便有了RenderObject。’
复制代码
我们可以看到。我们来看看方法:
...省略代码
markNeedsLayout();
markNeedsPaint();
void performLayout();
void performResize();
void paint(PaintingContext context, Offset offset)
...省略代码
复制代码
重构
我添加了三个类之后,评估了他们的职责:
widget 方便我构建图像
element 帮我增删改查
renderObject 帮我负责布局、绘制等
同时,由于element的职能,他应该持有Widget和Renderobject
复制代码
经过一系列的设计和重构,我的页面架构最终变成了这样:
这个完成widget、element、renderObject的基本职责。希望通过上面的文字大家能够对这三者有一个初步的了解。
作者:Jihada
来源:掘金
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。