按钮只报一个名字 — 按下去时宿主做了什么

5 分钟
目标画面课程第 4 讲。onTap 里没有代码,只有工具名。那个名字是否真的到了宿主,图上看不出来,所以测试台亲自按下去并记下来。

第 3 讲把值送进了画面。反方向还没讲。

onTap 里没有代码

{ "type": "button", "label": "Call next", "onTap": { "type": "tool", "tool": "queue.next" } },
{ "type": "button", "label": "Take a ticket", "onTap": { "type": "tool", "tool": "queue.take" } }

onTap 是 { type, tool }。「按这个会调用一个叫 queue.next 的工具」就是全部,那工具做什么并没有写。

也写不了。这个文件是服务器课程里做的那台服务器发来的,活儿在那台服务器上干。

接住的一侧

rt.buildUI(
  context: c,
  // 名字到这儿。宿主不知道这名字是什么意思。
  onToolCall: (tool, params) async => fired.add(tool),
)

这就是第 1 讲留成空函数的那个位置。在真实应用里,这里放的是客户端课程第 3 讲的一行。

onToolCall: (tool, params) async {
  // 名字来自画面。原样传过去。
  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);
},

tool 原样传过去。没有 if (tool == 'queue.next') 这样的分支 — 一旦加进去,服务器每多一个工具就得改一次宿主。

图上定不了这件事

到这里截图第一次不够用了。step4.png 上看得见两个按钮。看不见的是 它们被按下时会发出什么。

把 onTap 整个删掉,图还是一模一样。

所以测试台亲自按。

fired.clear();
await tester.tap(find.text('Call next'));
await tester.pump(const Duration(milliseconds: 40));
await tester.tap(find.text('Take a ticket'));
await tester.pump(const Duration(milliseconds: 40));
say('pressed -> the host was asked for: ${fired.join(", ")}');
pressed -> the host was asked for: queue.next, queue.take

fired 是 onToolCall 按顺序接到的名字。这一行落进 run.log,检查再把它读回来。

# 宿主若知道工具名,就失败
grep -rq 'queue\.next\|queue\.take' $SRC && \
  die "step4: the host knows the tool names"
# 按下去后,画面要求的名字没到,就失败
grep -q 'the host was asked for: queue.next, queue.take' $LOG || \
  die "step4: pressing did not send the tool names the screen asked for"

两行放在一起才有意义。只有上面一行,说的是「名字不在宿主里」;只有下面一行,名字写死在宿主里也照样通过。叠起来才是 画面给的名字原样发了出去。

删掉 onTap 再跑,下面那行会抓住。

   [5/8] step4 — the button names a tool and stops there
   step4: pressing did not send the tool names the screen asked for

失败不在画面上处理

queue.next 被拒了会怎样?服务器课程第 3 讲的叫号器,在没人等的时候会拒绝叫号。

拒绝也是结果。服务器返回 isError 和理由,它和别的值一起作为状态进来。

{ "type": "text", "text": "{{notice}}", "style": { "fontSize": 15, "color": "#9ca3af" } }

step4.json 靠下的这一行就是那个位置。画面定义里没有单独的错误处理语法 — 理由也不过是状态里的一个值。

渲染

step4  896 B, 12 lines, type "page" -> step4.png

43,下面 2 waiting,两个按钮,再下面灰色的 called 43。

自己跑一遍

cd content/sample/course-ui
bash verify.sh
cat captures/run.log

带走的

  1. onTap: { type: 'tool', tool: ... } — 画面只知道名字,到此为止
  2. onToolCall 原样传 — 不要按名字分支
  3. 会按下去的检查 — 截图看不见的,就写进日志

下一讲

最后一讲。一个画面变成两种形状 — 以及一个不知道自己有多长的列表。

运行示例

makemind-academy/course_ui/
git clone https://github.com/makemind-academy/course_ui
cd course_ui
bash verify.sh
在 GitHub 上打开 →