FLUTTER · 写给 Jetpack Compose 工程师

折纸

ZHÉ ZHǏ

你已经会一半了。剩下的一半,在那棵你看不见的树上。

你写过 Compose,知道界面是状态的函数,知道要把 UI 描述出来而不是命令它改。这些直觉一条都不用扔——Flutter 和 Compose 是一对表兄弟,连 ColumnRow 都同名。

但你也做过一点 Flutter,然后被一些说不清的事绊过:交换两个卡片,颜色没跟着走;随手包了一层 Padding,动画重来了一遍;把 ListView 放进 Column,App 当场红屏。这些不是零散的坑,它们全是同一件事的不同侧面——你还没见过那棵框架真正在维护的树。

六卷 24 17 个零依赖 Demo 五台真引擎 Flutter 3.44 · Dart 3.12 亮色排版

一句话说清这本书:你每一帧写下的只是一张平摊的纸——一份不可变的界面配置说明书,写完就被扔掉。框架照着上一帧留下的折痕,把这张新纸折成真正会量尺寸、会画像素的东西。

纸是一次性的(Widget),折痕是长命的(Element),折出来的立体物才是你在屏幕上看见的(RenderObject)。Compose 把折痕藏进了编译器插件和 slot table,Flutter 把它摆到了桌面上,还让你亲手管它——const、Key、BuildContext,全是管折痕的工具。学完这本书你会发现一件意外的事:Compose 你原来也才懂一半。

WIDGET · 纸
不可变的配置。每帧全部扔掉重建,这是设计意图,不是浪费。
ELEMENT · 折痕
框架真正维护的那棵树。State 挂在这里,所以它能活过重建。
RENDEROBJECT · 立体
真的在量、在画。建它很贵,所以框架拼命复用。

直觉清关表

这本书不假装你是新手。每一章开头都会拎出一条你从 Compose 带过来的直觉,当场盖章:直接放行、改一下、还是得扔掉。24 章 24 条,结果是这样的——

6
条直接放行。声明式的地基、Expanded、Stack、CompositionLocal、ViewModel 分层、列表回收,原样带过来。
9
条改一下还能用。大方向没错,但细节会咬人——Dart、State 回调、状态管理选型、async。
9
条得当场扔掉。其中 6 条来自同一个源头:你对「框架怎么知道什么变了」的想象。
◆ 这本书的两条主线

主线一:三棵树。Widget 为什么可以每帧扔掉?State 凭什么活得下来?canUpdate 只看 runtimeTypekey 这两个字段——一旦你接受这句话,Key 的坑、动画重来的坑、热重载为什么保得住状态、const 为什么是性能开关,会在同一秒钟一起讲通。

主线二:范围是谁划的。Compose 有编译器插件替你划重组范围,读了哪个状态就订阅哪个 scope,细到一个 Text。Flutter 没有这个编译器——setState 把整棵子树从那个 Element 往下重建一遍。于是「缩小重建范围」从编译器的工作,变成了你的手艺:拆 widget、加 const、把 Consumer 放到最小的那一块上。这本书后半本一直在讲这件手艺。

目录

卷 I · 认领 CLAIM你已经会一半了——先把带得过来的直觉认领回去
  1. 01你不是从零开始:Compose 与 Flutter 的血缘▸ 直觉清关台
  2. 02Dart:写给 Kotlin 工程师的两小时▸ Kotlin↔Dart
  3. 03Dart 3 的补货:记录、模式与 sealed▸ 模式匹配器
  4. 04const、final、late:const 是性能开关▸ const 规范化
卷 II · 三棵树 TREES那棵你看不见的树——本书主星卷
  1. 05Widget 不是视图,是每帧被扔掉的说明书
  2. 06三棵树:谁短命、谁长命、谁真的在画▸ 三树同步器
  3. 07canUpdate:Flutter 的 diff 只看两个字段▸ diff 步进
  4. 08Key:那个「换了位置颜色不跟着走」的坑▸ Key 开关
  5. 09State 的生命周期,与热重载为什么保得住状态▸ 生命周期
卷 III · 约束 CONSTRAINTS布局只有三句话,但你得真的信它
  1. 10约束向下,尺寸向上,父定位置▸ 约束求解器
  2. 11Row/Column:Expanded 与那条黄黑警戒条▸ Flex 分配
  3. 12无界约束:ListView 放进 Column 为什么炸▸ 无界报错
  4. 13剩下的 20%:Stack、Sliver、LayoutBuilder
卷 IV · 状态 STATECompose 的编译器帮你划范围,这里你自己划
  1. 14setState 到底标脏了谁▸ 重建计数器
  2. 15InheritedWidget:.of(context) 的 O(1) 魔法▸ context 查找
  3. 16Provider / Riverpod / Bloc:给你的小 App 选一个▸ 四种写法对比
  4. 17状态该放在哪:提升、Notifier 与 Repository
卷 V · 异步 ASYNC一根线程,凭什么不卡
  1. 18单线程为什么不卡:事件循环与微任务▸ 事件循环
  2. 19Future / Stream / async*:对照 suspend 与 Flow
  3. 20Isolate:真并行,以及你多半不需要它
卷 VI · 交付 SHIP把学校那个小 App 真的做出来、发出去
  1. 21导航:Navigator 这个栈,与 go_router▸ 路由栈
  2. 22网络、JSON 与本地存储:数据层怎么搭
  3. 23列表与滚动:builder 的回收,与卡顿排查▸ 视口回收
  4. 24交付清单:主题、平台通道、测试与上架
✎ 怎么读这本书

时间紧、下周就要开工:读卷 II(6–9 章)和卷 III(10–12 章),这七章解决你在 Flutter 里 80% 的「说不清为什么」。然后直接跳到卷 VI 抄工程骨架。

只想把印象加深:按顺序读,每章开头的那枚印章就是这一章的全部结论,正文是它的展开。

Demo 别只看:17 个 Demo 全是真跑的引擎——第 6 章那台会真的执行 Flutter 的 updateChild 算法,第 10 章那台会真的传播 BoxConstraints 并算出 RenderFlex overflowed by 500 pixels 这样的报错。正文里出现的每个数字,都是它们当场算的。