数字不在文件里 — 同一个文件,两张图
5 分钟目标画面课程第 3 讲。一个 `{{now}}` 就把画面和值分开了。至于是否真的分开,靠把同一个文件在两种状态下各渲染一次来定。
到第 2 讲为止,画面只显示文件里写着的东西。叫号屏这么做,号一变就得重发一份画面文件。
把位置空出来
{ "type": "page", "title": "Step 3",
"initialState": { "now": "0", "waiting": 0 },
"content": { "type": "center",
"child": { "type": "linear", "direction": "vertical", "spacing": 10, "alignment": "center",
"children": [
{ "type": "text", "text": "NOW SERVING", "style": { "fontSize": 15, "letterSpacing": 5, "color": "#6b7280" } },
{ "type": "text", "text": "{{now}}", "style": { "fontSize": 92, "fontWeight": "bold", "color": "#111827" } },
{ "type": "text", "text": "{{waiting}} waiting", "style": { "fontSize": 20, "color": "#6b7280" } } ] } } }多了两样。
initialState — 这个画面打开那一刻持有的值,免得一开就看见空白。
{{now}} — 把状态里的 now 放在这儿。可以整格用,也可以像 {{waiting}} waiting 那样嵌在句子里。
把值放进去
像 rt.stateManager.set('now', '42') 这样传键和值,用到那个键的节点就重画。宿主不知道是哪些节点,也不需要知道。
接上客户端课程做的东西,连一个个塞都不必了。
onToolCall: (tool, params) async {
// 服务器返回的 map 原样倒进去
final r = await client.callTool(tool, params);
final state = jsonDecode((r.content.first as TextContent).text)
as Map<String, dynamic>;
state.forEach(rt.stateManager.set);
},服务器课程第 3 讲让工具返回 {"now": ..., "waiting": ...}。那份 map 的键和画面上 {{...}} 的名字一致时,中间不需要转换代码。
怎么判断绑定是真的
写了 {{now}},画面上出现 42,就当成了 — 这是容易犯的错。文件里写死 42,画面上也会出现 42。
所以检查分两路。
# 值若被写死进文件,就失败
grep -qE '"(42|7)"' screens/step3.json && die "step3: the value is baked in"
grep -q '{{now}}' screens/step3.json || die "step3: nothing is bound"
# 一个文件,两种状态。图不动,那个绑定就是装饰。
cmp -s captures/step3.png captures/step3_alt.png && \
die "step3: the same file rendered the same picture for two different states"测试台把 step3.json 渲染两次:一次 now: 42, waiting: 3,一次 now: 7, waiting: 0。同一个文件。
step3 587 B, 8 lines, type "page" -> step3.png
step3_alt 587 B, 8 lines, type "page" -> step3_alt.pngstep3 one file, two states, two pictures (27577 B vs 24734 B)两张 PNG 不同。文件只有一个,那让它们不同的就是状态。
检查确实抓得住,这一点验过
全通过的检查什么也证明不了。所以把 {{now}} 换成 42 跑了一次。
[4/8] step3 — the number is not in the file
step3: the value is baked in抓住了。第 4、第 5 讲的检查也各破坏一次,三处都抓住了。
多少东西该放进状态
NOW SERVING 是固定文字。本可以抽成 {{label}} 放进状态。
没有抽。放进状态意味着 每次都得有人去填。 漏填,那块就是空的。不变的文字留在文件里,会少一处可能出错的地方。
判据:决定这个值的一方在画面之外,就是状态;否则就写在文件里。
自己跑一遍
cd content/sample/course-ui
bash verify.sh
open captures/step3.png captures/step3_alt.png带走的
initialState+{{key}}— 空出位置,给它一个名字stateManager.set— 宿主只知道键和值- 两种状态两张截图 — 图动了,才叫绑定
下一讲
值进来了。接下来是出去的一侧 — 按下按钮时发生了什么。
运行示例
makemind-academy/course_ui/git clone https://github.com/makemind-academy/course_ui cd course_ui bash verify.sh