换个角度——Widget、Element和RenderObject的由来

49 篇文章 0 订阅
47 篇文章 0 订阅

前言

这个也算是老生常谈了,网上有很多关于这三个类(树)的介绍。实际上,可能在我们初学flutter时,就看过类似的文章介绍。

如下图必是见过多次了

可能由于这些文章的切入点不同(或本人愚钝),我在初学(甚至入门一段时间后)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(轮子)的便捷,我只用了一天的功夫,就创建了一个绚丽页面:

它还能交互:

点击 圆的时候,两个圆颜色互换
点击 方块时,黄色变成了白色
点击 三角时,三角就消失了,再点击空白位置会复现。

为了实现这个功能,我在widget里加了一大堆的标识(Flag),用来标志哪些需要更新、删除、插入等,同时添加了函数用来实现对应的功能。

一切完毕后,我的代码已经一团乱麻,难以阅读——可谓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

element的加入,让我的页面代码清爽了不少,同时我的页面也增加了新的酷炫功能:

双击页面后,会出现一个灰色矩形,盖住原来的图形,并且左右晃动。

功能写完,测试机一跑,我的老爷机直接蓝屏。

这可不行,给老板申请资金是甭指望了,为了省钱,他还用算盘算账呢… 只能动手优化代码了。

经过观察,我发现了几处可以优化的地方:

1, 之前的‘方块’和‘三角’被遮挡了,那么再画它们也没啥意义,布局也可以省了

2, 上面两个圆,就没动过,所以布局啊、绘制啥的都没必要再跑一遍

3, 灰色方块,大小没变,颜色也没变,我觉得只要调整它的位置就行

先加入7、8 匙Flag,再放入10来勺函数…

…一阵忙活后,在老爷机上完美运行。

不过我的代码又变的臃肿不堪了,只好像前面的element一样再抽出一个类来帮我管理绘制、布局、指令录制(实际封装在PaintContext)什么的了:

‘于是,便有了RenderObject。’

我们可以看一下它的方法:

...省略代码

markNeedsLayout();
markNeedsPaint();
void performLayout();
void performResize();
void paint(PaintingContext context, Offset offset)

...省略代码

重构

三个类添加完后,我回溯了一下他们的职责:

widget  方便我构建图像
element 帮我增删改查
renderObject 帮我负责布局、绘制等

同时,由于element的职能,他应该持有Widget和Renderobject

经过一些列的设计、重构,我的页面架构最终变成了这个样子:

至此 widget、element和renderObject的基本职责就说完了,希望借由上面的文字,你能对它们三个有所初步的了解。

谢谢大家阅读。

系列文章

Flutter——仿网易云音乐App(基础版)

实现网易云音乐的滑动冲突处理效果

Flutter自定义View——仿高德三级联动Drawer

Flutter 自定义View——仿同花顺自选股列表

Flutter入门练习——Evenet&Method Channel协作加载大图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值