画面就是文件 — 四行开始的 UI 运行时
5 分钟目标画面课程第 1 讲。客户端课程第 4 讲里服务器送来的那团东西,究竟是什么。从一个四行的 JSON、和一个绝不往里看的宿主开始。
客户端课程第 4 讲里画面到了。读 ui://desk 拿回 546 字节,客户端把它当作字符串收下,数了长度就结束了。
这一条线讲的是另一半:那串字符怎么变成画。
四行
{ "type": "page", "title": "Step 1",
"content": { "type": "center",
"child": { "type": "text", "text": "The whole screen is this file",
"style": { "fontSize": 34, "fontWeight": "bold", "color": "#111827" } } } }233 字节。这就是画面的全部。三个节点。
page— 一个画面,带标题。center— 把唯一的子节点放到中间。text— 文字。
是一棵树。page 里有一个 content,center 里有一个 child。装多个子节点的节点在第 2 讲出现。
画它的那一侧
final rt = MCPUIRuntime(enableDebugMode: false);
// screen = jsonDecode(文件内容)
await tester.runAsync(() => rt.initialize(screen));
await tester.pumpWidget(RepaintBoundary(
key: key,
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Builder(
builder: (c) => rt.buildUI(
context: c,
onToolCall: (tool, params) async => fired.add(tool),
),
),
),
));把 map 交给 initialize,buildUI 返回一个 widget。中间没有关于画面的任何判断。
这个宿主跑在测试里,是为了能截图。在真实应用里,runApp 站在 pumpWidget 的位置 — 上下两行不变。
onToolCall 到第 4 讲才用。现在只是把名字记下来。
要点在于「什么不在」
这条线的宿主只有一个文件:host/test/capture_test.dart。五讲下来它几乎不变 — 五个画面都由同一个宿主来画。
所以这门课最重要的检查不是「有没有」,而是 「有没有不在」。
# 画面上看得见的字若在程序里,就失败。
grep -rq 'The whole screen is this file' $SRC && \
die "step1: the words on the screen are inside the host"
# 宿主若在手写 widget,就失败。
grep -rqE '\bText\(|\bColumn\(|\bRow\(|\bElevatedButton\(' $SRC && \
die "step1: the host builds widgets by hand"$SRC 是 host/test host/pubspec.yaml。host/build 被排除了 — 编译产物里原样嵌着这些字符串,扫进去这项检查就永远通过。第 4 讲的检查刚接上时,正是因此误报过一次。
空的图也是图
截图由 RepaintBoundary.toImage 得到。画面完全没搭起来也照样出 PNG — 一张全白的。
所以不能只看文件在不在。
for s in step1 step2 step3 step3_alt step4 step5_empty step5_filled; do
[ -s "captures/$s.png" ] || die "$s: no capture"
# 没搭起来的画面也会留下 PNG — 空的那种。
[ "$(wc -c <captures/$s.png)" -gt 5000 ] || die "$s: the capture is blank"
donestep1.png 是 25,196 字节。一块白板会掉到五千以下。
渲染
step1 233 B, 4 lines, type "page" -> step1.png1000×640 逻辑像素,2 倍。淡紫底色正中,一行粗黑字。
这句话在 capture_test.dart 里哪儿都没有。
自己跑一遍
cd content/sample/course-ui
bash verify.sh
open captures/step1.png带走的
page→content→child— 画面是一棵树initialize+buildUI— 宿主的活儿只有这两件- 检查「不在」 — 只有确认画面内容不在程序里,「画面就是文件」才成立
下一讲
一个节点成不了画面。多个该放在哪儿 — 而且不用坐标。
运行示例
makemind-academy/course_ui/git clone https://github.com/makemind-academy/course_ui cd course_ui bash verify.sh