<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flutter on opoojkk</title><link>https://opoojkk.com/tags/flutter/</link><description>Recent content in Flutter on opoojkk</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Sun, 31 Aug 2025 11:56:40 +0800</lastBuildDate><atom:link href="https://opoojkk.com/tags/flutter/index.xml" rel="self" type="application/rss+xml"/><item><title>flutter渲染和布局</title><link>https://opoojkk.com/posts/2025/08/31/flutter-rendering-and-layout/</link><pubDate>Sun, 31 Aug 2025 11:56:40 +0800</pubDate><guid>https://opoojkk.com/posts/2025/08/31/flutter-rendering-and-layout/</guid><description>&lt;blockquote>
&lt;p>原文：&lt;a href="https://docs.flutter.dev/resources/architectural-overview#rendering-and-layout">Flutter architectural overview | Flutter&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>本节描述了Flutter的渲染管道，即将一层层的Widgets层级转换为实际绘制在屏幕上的像素的系列步骤。&lt;/p>
&lt;h3 class="heading" id="flutter的渲染模型">
 Flutter的渲染模型&lt;span class="heading__anchor"> &lt;a href="#flutter%e7%9a%84%e6%b8%b2%e6%9f%93%e6%a8%a1%e5%9e%8b">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>你可能会好奇：如果Flutter是一个跨平台框架，它是如何实现和单平台框架接近的性能呢？&lt;/p>
&lt;p>首先了解传统Android应用的工作方式是很有帮助的。当进行绘制时，你会先调用Android框架的Java代码。Android系统库提供了一些组件，它们负责将内容绘制到一个&lt;code>Canvas&lt;/code>对象上，之后Android会调用&lt;a href="https://skia.org/">Skia&lt;/a>（一个用C/C++编写的图形引擎）通过CPU或GPU将内容绘制到设备上。&lt;/p>
&lt;p>跨平台框架&lt;em>通常&lt;/em>通过在底层的原生Android和iOS UI库之上创建一个抽象层来工作，试图平滑处理各个平台UI表示之间的不一致。应用代码通常用解释型语言（如JavaScript）编写，并且必须与Java语言的Android库或Objective-C语言的iOS系统库交互来呈现UI。所有这些会增加显著的开销，尤其是在应用的UI与逻辑有大量交互时。&lt;/p>
&lt;p>相比之下，Flutter通过尽量减少这些抽象层来提升性能。在其设计中，Flutter绕过原生的系统UI部件库，直接使用自己的插件化小组件集合（Widget）。Flutter的Dart代码会被编译为原生代码，并利用名为&lt;strong>Impeller&lt;/strong>的渲染引擎进行渲染。Impeller随应用一起提供，这使开发者即使在终端设备没有升级到最新系统版本时，也能依赖Flutter框架轻松推进应用性能的改进。这种方式同样适用于Flutter运行在其他原生平台上（如Windows或macOS）。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>注意&lt;/strong>
想了解Impeller支持哪些设备？请查看&lt;a href="https://flutter.dev/go/can-i-use-impeller">我可以使用Impeller吗？&lt;/a>。更多内容请访问&lt;a href="https://docs.flutter.dev/perf/impeller">Impeller 渲染引擎&lt;/a>。&lt;/p>
&lt;/blockquote>
&lt;h3 class="heading" id="从用户输入到gpu">
 从用户输入到GPU&lt;span class="heading__anchor"> &lt;a href="#%e4%bb%8e%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e5%88%b0gpu">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>Flutter的渲染管道遵循的核心原则是&lt;strong>简单即高效&lt;/strong>。Flutter为数据流向系统的过程设计了一个简洁的管道，如下图所示：&lt;/p>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/render-pipeline.png" alt="渲染管道示意图">&lt;/figure>&lt;p>接下来我们将更详细地了解其中一些阶段。&lt;/p>
&lt;hr>
&lt;h3 class="heading" id="构建阶段从widget到element">
 构建阶段：从Widget到Element&lt;span class="heading__anchor"> &lt;a href="#%e6%9e%84%e5%bb%ba%e9%98%b6%e6%ae%b5%e4%bb%8ewidget%e5%88%b0element">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>考虑以下一个展示组件层级的代码片段：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Container&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">color:&lt;/span> &lt;span class="n">Colors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">blue&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">child:&lt;/span> &lt;span class="n">Row&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">children:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Image&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">network&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https://www.example.com/1.png&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">const&lt;/span> &lt;span class="n">Text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;A&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>当Flutter需要渲染这一片段时，它会调用&lt;code>build()&lt;/code>方法，该方法返回一棵基于当前应用状态的UI子树。构建过程中，&lt;code>build()&lt;/code>方法可以根据状态引入新的组件。例如，在上述代码片段中，&lt;code>Container&lt;/code>具有&lt;code>color&lt;/code> 和 &lt;code>child&lt;/code> 属性。查看&lt;a href="https://github.com/flutter/flutter/blob/02efffc134ab4ce4ff50a9ddd86c832efdb80462/packages/flutter/lib/src/widgets/container.dart#L401">Container 的源码&lt;/a>，可以发现当&lt;code>Color&lt;/code>不为空时，它会插入一个代表此颜色的&lt;code>ColoredBox&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">color&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ColoredBox&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nl">color:&lt;/span> &lt;span class="n">color&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nl">child:&lt;/span> &lt;span class="n">current&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>类似地，&lt;code>Image&lt;/code>和&lt;code>Text&lt;/code>组件在构建过程中可能会插入子组件，例如&lt;code>RawImage&lt;/code>和&lt;code>RichText&lt;/code>。因此真实的组件层级可能比代码表面上看起来更深，如下所示&lt;sup>&lt;a href="https://docs.flutter.dev/resources/architectural-overview?utm_source=chatgpt.com#fn2">[2]&lt;/a>&lt;/sup>：&lt;/p>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/widgets.png" alt="小部件层级树示意图">&lt;/figure>&lt;p>这解释了为什么通过调试工具（如&lt;a href="https://docs.flutter.dev/tools/devtools/inspector">Flutter inspector&lt;/a>）检查组件树时，你可能看到的结构要比你代码中写出的深得多。&lt;/p>
&lt;p>在构建阶段，Flutter将代码中表达的组件翻译为对应的&lt;strong>Element 树&lt;/strong>，每个组件都有一个对应的Element实例。每个Element代表组件树某个位置上某个组件的特定实例。Element主要有两种类型：&lt;/p>
&lt;ul>
&lt;li>&lt;code>ComponentElement&lt;/code>：用于承载其他元素。&lt;/li>
&lt;li>&lt;code>RenderObjectElement&lt;/code>：参与布局或绘制阶段的元素。&lt;/li>
&lt;/ul>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/widget-element.png" alt="Widget和Element关系示意图">&lt;/figure>&lt;p>&lt;code>RenderObjectElement&lt;/code>是其对应的Widget和底层&lt;code>RenderObject&lt;/code>之间的中介。&lt;/p>
&lt;p>任何组件的Element可以通过它的&lt;code>BuildContext&lt;/code>进行引用，&lt;code>BuildContext&lt;/code>是树中组件位置的句柄。例如，在调用&lt;code>Theme.of(context)&lt;/code>时使用的&lt;code>context&lt;/code>，就是传递给&lt;code>build()&lt;/code>方法的参数。&lt;/p>
&lt;p>由于Flutter的组件是不可变的（包括节点间的父/子关系），任何对组件树的更改（如将&lt;code>Text('A')&lt;/code>改成&lt;code>Text('B')&lt;/code>）都会返回一组新的组件对象。这并不意味着底层表示会完全重建。Element树从一个渲染帧到下一帧是持久化的，这在性能优化中起着重要作用。Flutter只会遍历发生变化的Widget，重新配置需要调整的Element树部分。&lt;/p>
&lt;hr>
&lt;h3 class="heading" id="布局和渲染">
 布局和渲染&lt;span class="heading__anchor"> &lt;a href="#%e5%b8%83%e5%b1%80%e5%92%8c%e6%b8%b2%e6%9f%93">#&lt;/a>&lt;/span>
&lt;/h3>&lt;p>在一个应用中，仅绘制单个组件的情况很少见。因此，任何UI框架的重要特性之一就是高效地布局整个组件树，确定每个元素在屏幕上的大小和位置。&lt;/p>
&lt;p>每个渲染树节点的基础类是&lt;a href="https://api.flutter.dev/flutter/rendering/RenderObject-class.html">&lt;code>RenderObject&lt;/code>&lt;/a>，它定义了一个抽象模型用于布局和绘制。这个模型非常通用：它并不限定维度的数量，甚至不固定使用直角坐标系（例如&lt;a href="https://dartpad.dev/?id=596b1d6331e3b9d7b00420085fab3e27">使用极坐标系统的示例&lt;/a>）。每个&lt;code>RenderObject&lt;/code>只了解它的父节点，但对子节点知之甚少，除了知道如何“访问”它们以及它们的约束条件。这种抽象使得&lt;code>RenderObject&lt;/code>能够应对多种用例。&lt;/p>
&lt;p>在构建阶段，Flutter会为Element树中的每个&lt;code>RenderObjectElement&lt;/code>创建或更新一个继承自&lt;code>RenderObject&lt;/code>的对象。&lt;code>RenderObject&lt;/code>是基础的渲染单位，例如：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://api.flutter.dev/flutter/rendering/RenderParagraph-class.html">&lt;code>RenderParagraph&lt;/code>&lt;/a>：用于渲染文本；&lt;/li>
&lt;li>&lt;a href="https://api.flutter.dev/flutter/rendering/RenderImage-class.html">&lt;code>RenderImage&lt;/code>&lt;/a>：用于渲染图像；&lt;/li>
&lt;li>&lt;a href="https://api.flutter.dev/flutter/rendering/RenderTransform-class.html">&lt;code>RenderTransform&lt;/code>&lt;/a>：对子组件进行变换后绘制。&lt;/li>
&lt;/ul>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/trees.png" alt="Widget、Element 和 RenderObject 的区别">&lt;/figure>&lt;p>大多数Flutter组件由继承自&lt;code>RenderBox&lt;/code>的对象进行渲染，&lt;code>RenderBox&lt;/code>是二维笛卡尔空间中固定大小的渲染对象。&lt;code>RenderBox&lt;/code>提供了一个&lt;strong>盒约束模型&lt;/strong>，规定了每个组件在渲染时的最小和最大宽高。&lt;/p>
&lt;p>在布局时，Flutter会以深度优先的顺序遍历渲染树，并从父节点向子节点&lt;strong>向下传递大小约束&lt;/strong>。子节点在确定其大小时，必须遵守父节点提供的约束。子节点随后会通过向父节点 &lt;strong>向上传递大小&lt;/strong> 进行响应，并且约束在子节点中得到了应用。&lt;/p>
&lt;figure>
 &lt;img src="https://docs.flutter.dev/assets/images/docs/arch-overview/constraints-sizes.png" alt="约束向下传递，大小向上传递">&lt;/figure>&lt;p>在单次遍历树之后，每个对象已根据父节点的约束条件定义了自己的大小，并准备通过调用&lt;a href="https://api.flutter.dev/flutter/rendering/RenderObject/paint.html">&lt;code>paint()&lt;/code>&lt;/a>方法进行绘制。&lt;/p>
&lt;p>盒约束模型是一种强大的组件布局机制，其效率可达&lt;em>O(n)&lt;/em> ：&lt;/p>
&lt;ul>
&lt;li>父组件可以通过将最大和最小尺寸设置为相同的值来完全控制一个子组件的尺寸。例如，顶层渲染对象会将其子组件的尺寸约束限定为整个屏幕大小，（子组件可以选择如何利用该空间，例如将要渲染的内容居中显示）。&lt;/li>
&lt;li>父节点可以限制子节点的宽度，同时允许其对高度有一定的弹性（或相反）。一个实际的例子是流式文本，它可能需要适应水平约束但可以根据文本数量调整垂直方向。&lt;/li>
&lt;/ul>
&lt;p>即使在某些情况下子对象需要知道可用空间大小以决定内容的呈现方式，盒约束模型也能够正常工作。通过使用&lt;a href="https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html">&lt;code>LayoutBuilder&lt;/code>&lt;/a>组件，子对象可以检查传递下来的约束并依据约束决定其呈现方式，例如：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Widget&lt;/span> &lt;span class="n">build&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BuildContext&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">LayoutBuilder&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">builder:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">constraints&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">constraints&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">maxWidth&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="m">600&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kd">const&lt;/span> &lt;span class="n">OneColumnLayout&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kd">const&lt;/span> &lt;span class="n">TwoColumnLayout&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>更多关于约束与布局系统的信息和示例，请参考&lt;a href="https://docs.flutter.dev/ui/layout/constraints">理解约束&lt;/a>。&lt;/p>
&lt;p>所有&lt;code>RenderObject&lt;/code>的根节点是&lt;code>RenderView&lt;/code>，它代表整个渲染树的输出。当平台要求新帧被渲染时（例如由于&lt;a href="https://source.android.com/devices/graphics/implement-vsync">垂直同步（vsync）&lt;/a>或纹理解压缩/上传完成），会调用渲染树根节点&lt;code>RenderView&lt;/code>对象的&lt;code>compositeFrame()&lt;/code>方法。这会创建一个&lt;code>SceneBuilder&lt;/code>以触发场景更新。当场景完成后，&lt;code>RenderView&lt;/code>对象会将合成的场景传递给&lt;code>dart:ui&lt;/code>中的&lt;code>Window.render()&lt;/code>方法，并将控制权交给GPU以完成渲染。&lt;/p>
&lt;p>关于渲染管道中合成和光栅化阶段的详细内容超出了本高层次概述的范围，更多信息可参见&lt;a href="https://www.youtube.com/watch?v=UUfXWzp0-DU">Flutter渲染管线的视频演讲&lt;/a>。&lt;/p></description></item></channel></rss>