Flutter 核心原理与混合开发模式

本文深入解析Flutter的构建、布局、绘制和合成流程,阐述其高性能的秘诀。同时,探讨了Flutter的混合开发模式,包括Web容器、泛Web容器、自绘引擎的优缺点,并详细介绍了Flutter Boost、Flutter Thrio等混合框架的工作原理,以及多Engine和View级别的混合模式。文章最后触及了Flutter的编译模式,如JIT和AOT,为开发者提供了全面的Flutter开发知识。
摘要由CSDN通过智能技术生成

抛开 Diff 和 Render 我们本文不讲解,因为这两部分稍稍繁琐一些,我们来关注下剩下的四个环节。

绘制流程

注:此流程图出自 复杂业务如何保证Flutter的高性能高流畅度?| 闲鱼技术,可以较为清晰的表达 Flutter 核心的绘制流程了。

1.4.1 Build

执行 build 方法时,根据组件的类型,存在两种不同的逻辑。

我们知道,Flutter 内的 Widget 可以分为 StatelessWidget 与 StatefulWidget,即无状态组件与有状态组件。

所谓 StatelessWidget,就是它 build 的信息完全由配置参数(入参)组成,换句话说,它们一旦创建成功就不再关心、也不响应任何数据变化进行重绘。

StatelessWidget

所谓 StatefulWidget,除了父组件初始化时传入的静态配置之外,还要处理用户的交互与内部数据变化(如网络数据回包)并体现在 UI 上,这类组件就需要以 State 类打来 Widget 构建的设计方式来实现。它由 State 的 build 方法构建 UI, 最终调用 buildScope 方法。其会遍历 _dirtyElements,对其调用 rebuild/build。

StatefulWidget

注:以上两图出自 《Flutter 核心技术与实战 | 陈航》

1.4.2 Layout

只有布局类 Widget 会触发 layout(如 Container、Padding、Align 等)。

每个 RenderObject 节点需要做两件事:

  1. 调用自己的 performLayout 来计算 layout

  2. 调用 child 的 layout,把 parent 的限制传入

/// 实际计算 layout 的实现void performLayout() { _size = configuration.size; if (child != null) { child.layout(BoxConstraints.tight(_size)); }}void layout(Constraints constraints, { bool parentUsesSize = false }) { /// …省略无关逻辑 RenderObject relayoutBoundary; if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) { relayoutBoundary = this; } else { relayoutBoundary = (parent as RenderObject)._relayoutBoundary; } if (!_needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) { return; } _constraints = constraints; _relayoutBoundary = relayoutBoundary; if (sizedByParent) { performResize(); } RenderObject debugPreviousActiveLayout; performLayout(); markNeedsSemanticsUpdate(); _needsLayout = false; markNeedsPaint();}

如此递归一轮,每个节点都受到父节点的约束并计算出自己的 size,然后父节点就可以按照自己的逻辑决定各个子节点的位置,从而完成整个 Layout 环节。

layout

1.4.3 Paint

渲染管道中首先找出需要重绘的 RenderObject,如果有实现了 CustomPainter 则调用 CustomPainter paint 方法 再调用 child 的 paint 方法;如果未实现 CustomPainter,则直接调用 child 的 paint。

在调用 paint 的时候,经过一串的转换后,layer->PaintingContext->Canvas,最终 paint 就是描绘在 Canvas 上。

void paint(PaintingContext context, Offset offset) { if (_painter != null) { // 只有持有 CustomPainter 情况下,才继续往下调用自定义的 CustomPainter 的 paint 方法,把 canvas 传过去 _paintWithPainter(context.canvas, offset, _painter); _setRasterCacheHints(context); } super.paint(context, offset); //调用父类的paint的方法 if (_foregroundPainter != null) { _paintWithPainter(context.canvas, offset, _foregroundPainter); _setRasterCacheHints(context); }}// 在父类的 paint 里面继续调用 child 的 paint,实现父子遍历void paint(PaintingContext context, Offset offset) { if (child != null){ context.paintChild(child, offset); }void _paintWithPainter(Canvas canvas, Offset offset, CustomPainter painter) { int debugPreviousCanvasSaveCount; canvas.save(); if (offset != Offset.zero) canvas.translate(offset.dx, offset.dy); // 在调用 paint 的时候,经过一串的转换后,layer->PaintingContext->Canvas,最终 paint 就是描绘在 Canvas 上 painter.paint(canvas, size); /// … canvas.restore();}

1.4.4 Composite

合成主要做三件事情:

  1. 把所有 Layer 组合成 Scene

  2. 通过 ui.window.render 方法,把 Scene 提交给 Engine。

  3. Engine 把计算所有的 Layer 最终的显示效果,渲染到屏幕上。

final ui.Window _window;void compositeFrame() { // 省略计时逻辑 final ui.SceneBuilder builder = ui.SceneBuilder(); final ui.Scene scene = layer.buildScene(builder); if (automaticSystemUiAdjustment) _updateSystemChrome(); _window.render(scene); scene.dispose();}void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) { addChildrenToScene(builder);}void addChildrenToScene(ui.SceneBuilder builder, [ Offset childOffset = Offset.zero ]) { Layer child = firstChild; while (child != null) { if (childOffset == Offset.zero) { child._addToSceneWithRetainedRendering(builder); } else { child.addToScene(builder, childOffset); } child = child.nextSibling; }}

2. 跨端方案对比


跨端开发是必然趋势,从本质上来说,它增加业务代码的复用率,减少因为适配不

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值