关于 FlutterSDK Dusk
FlutterSDK Dusk 是用于运行中 Flutter 应用的端到端驱动工具,提供 CLI 和 stdio MCP 服务。它面向开发者与编码 Agent,可读取 Semantics 树并执行界面操作,无需编写测试文件。
主要功能
- 01Semantics 树快照
- 02点击、输入与滚动
- 03截图与运行状态观察
- 04CLI 与 MCP 工具
- 05操作前可操作性检查
- 06支持热重载后快照
适用场景
- 01检查运行中应用的界面结构
- 02自动点击和填写应用控件
- 03截图验证交互结果
- 04通过 Agent 调试 Flutter 应用
MCP 相关
运行时 MCP 服务通过 stdio 提供 39 个 `dusk_*` 工具,用于操作运行中的 Flutter 应用。它与托管文档 MCP 的 `search-docs` 工具不同,后者用于查询 Dusk 文档。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Dusk
Flutter 应用的端到端驱动程序。便于人类阅读,由 AI Agent 驱动。
通过一个 CLI 和一个 stdio MCP 服务,即可快照 Semantics 树、执行任意手势、截取屏幕截图并观察实时状态。
说明
需要 Dart SDK >= 3.4.0 和 Flutter >= 3.22.0。dusk 仅用于调试:生产构建会在 Web、桌面和移动平台上通过 tree-shake 移除整个驱动程序。
安装
flutter pub add fluttersdk_dusk
dart run fluttersdk_dusk dusk:install # patches lib/main.dart + scaffolds ./bin/fsa + registers dusk plugin
就这么简单。执行第二条命令后,全部 41 个 dusk:* 命令都可以通过 ./bin/fsa <cmd>(预热后约 110ms)或 dart run fluttersdk_dusk <cmd>(效果相同,启动约 3s)调用;只需再执行 dart run fluttersdk_dusk mcp:install 即可安装 MCP 服务。如果没有 fastcli(./bin/fsa),mcp:install 会自动将 dart run fluttersdk_dusk mcp:serve 写入 .mcp.json,无需手动编辑。各客户端的接入方式请参阅 MCP 安装:8 个客户端。手动接入、Magic-stack 集成以及完整的各命令标志参考,请参阅入门指南。
提示
fastcli 是 AOT 编译的 artisan 分发器(预热后约 110ms),随
./bin/fsa一同提供。dusk:install会自动为你搭建它(尽力而为;如果dart不在 PATH 中,则会改为发出警告)。之后,每条./bin/fsa <cmd>命令都是快速路径。CLI 也可以通过dart run fluttersdk_dusk <cmd>运行,命令接口相同,但每次调用约慢 3s。
为什么选择 Dusk?
Flutter 端到端测试一直像是一场东拼西凑的仪式。flutter_driver 提供了一次性套接字协议,热重启后就无法继续使用。integration_test 在进程内通过模拟的 WidgetTester 运行,但你需要编写测试文件、构建、运行,然后等待。想要驱动正在运行的应用的 AI 编码 Agent,只能临时调用 flutter test,把堆栈跟踪复制到提示词中,再把屏幕截图粘贴回来,还把这一套称作工作流。
Dusk 打通了这一闭环。 一组 VM Service 扩展(ext.dusk.*)、一个 CLI 命名空间(dusk:*),以及一个 stdio MCP 服务,为 41 个 CLI 命令和 39 个 MCP 工具提供支持(33 个 ext.dusk.* + 6 个通过基底路由的 artisan:dusk:*)。同一套契约既支持人工在终端中调用,也支持 Agent 通过 MCP 工具调用:dusk:tap --ref=e7(CLI)和 dusk_tap(MCP)走的是完全相同的代码路径。无需测试框架、无需测试文件、无需构建。连接到正在运行的应用,Agent 就有了眼睛(dusk_snap、dusk_screenshot、dusk_observe)和双手(dusk_tap、dusk_type、dusk_scroll、dusk_drag)。在 Flutter Web 上,如果 artisan 使用 --cdp-port 启动,dusk:screenshot CLI 命令会在进程内扩展因 CanvasKit 而卡住时,回退到 CDP Page.captureScreenshot;dusk_screenshot MCP 工具仍会在进程内分发,因此 Web Agent 应使用 CLI 截图。dusk_exceptions 会在包内捕获非致命的 FlutterError(包括溢出错误),因此即使没有 telescope,诊断功能也能正常工作。
| 工具 | 功能 | Dusk 的定位 |
|---|---|---|
| integration_test(Flutter SDK) | 进程内的 WidgetTester 封装;通过 flutter drive 运行测试 | 定位不同:integration_test 负责编写好的测试文件;Dusk 负责未编写脚本的运行中应用。两者可以一起使用。 |
| patrol(Leancode,694 个赞) | 在 integration_test 上叠加原生 UI 权限处理和对话框 | 互补:patrol 负责带原生对话框的编写好的测试;Dusk 负责由人工和 AI Agent 执行的无脚本自动化。 |
| flutter_driver(Flutter SDK,旧版) | 一次性套接字协议;正在逐步淘汰 | Dusk 通过 registerExtensionIdempotent(21 个聚合器注册函数)支持热重启;CLI 和 MCP 共用一套契约,无需测试框架,也无需单独的隔离区。 |
| maestro(13.7K 星) | 基于操作系统无障碍层的 YAML DSL | Dusk 直接驱动 Flutter Widget 树(Semantics 节点和 RenderObjects);不受 Flutter Desktop 限制;无需编写任何 YAML。 |
| mcp_flutter(298 星) | 带有 fmt_* 工具的 Flutter MCP 工具包 | Dusk 已发布到 pub.dev,发布者经过验证;提供框架原生 Artisan 插件、e<N> 和 q<N> 引用系统,以及 6 步可操作性门控。 |
| playwright-mcp(33K 星) | 通过无障碍树和 [ref=eN] 标记实现的浏览器 MCP | 对应的 Flutter 原生方案。Dusk 的 q<N> 句柄与 Playwright 的 getByRole() 相对应;dusk_observe 借鉴了 Stagehand 的一次观察、多次操作模式,并将其移植到 Flutter Semantics。 |
功能
| 功能 | 描述 | |
|---|---|---|
| 🌳 | 语义快照 | dusk_snap 会输出带有稳定 [ref=eN] 标记的 YAML 树;每个操作都以 ref 为目标,无需使用脆弱的 XPath 或猜测坐标 |
| 🛠️ | 41 个 CLI 命令 | snap、tap、type、fill、drag、scroll、hover、dblclick、right_click、triple_click、focus、blur、clear、set_checkbox、select_option、press_key、wait、find、observe、navigate、navigate_back、get_routes、modal、reset_overlays、screenshot、hot_reload_and_snap、CDP resize + device、close_app、install、doctor、wait_for_network_idle、console、exceptions、perf_begin + perf_end + perf_insight + perf_run + perf_compare + perf_trace + perf_campaign |
| 🤖 | 39 个 MCP 工具 | 完整的 CLI 功能集,外加 dusk_evaluate;以 stdio JSON-RPC 工具的形式提供给 Claude Code、Cursor、Windsurf、VS Code Copilot 及任何兼容 MCP 的 Agent |
| 🚪 | 6 步可操作性门控 | 每个手势都会经过 defunct(预检)→ enabled → zero-rect → off-viewport(自动滚动)→ stable(矩形连续 2 帧偏移检测)→ receives-events(命中测试路径);不会出现不稳定的点击。如果最后一项无法得到验证,会返回 checks 区块,而不是静默通过 |
| 🔖 | Playwright 风格定位器 | dusk_find 返回可重新解析的 q<N> 句柄,每次操作都会遍历实时 Semantics 树。过期句柄会抛出错误,不会静默地作用于错误的控件 |
| 🔄 | 热重载 + 快照往返 | dusk_hot_reload_and_snap 一次调用即可返回 {reloaded, durationMs, snapshot, screenshot, exceptions} |
| 🖥️ | CDP 设备模拟 | dusk_resize_viewport 和 dusk_device_profile(iphone-x、pixel-5、desktop-1440,另有 5 种)通过 Chrome DevTools Protocol 驱动 |
| 📈 | 帧归因会话 | dusk_perf_begin / dusk_perf_end 包围一次由驱动执行的交互,并报告构建了哪些控件类型、构建频率和耗时,同时提供 wind 的缓存命中/未命中/绕过计数器及 magic 的控制器通知。如果引擎没有通过该会话进行渲染,则会拒绝报告,而不是给出一张读起来像是“很快”的全零表格 |
| 🎨 | 快照增强器插件 | DuskPlugin.enrichers.add() 允许 magic 和 wind 通过冻结的 String? Function(Element, RefRegistry) 契约添加框架专属的 YAML 片段 |
| 🔒 | 发布版摇树优化 | 使用方将 DuskPlugin.install() 包在 !kReleaseMode 中,这样调试版和 profile 版会包含驱动;发布版则会在 Web、桌面和移动端对其进行摇树优化 |
| 📡 | AI 优先分发 | 标准的 fluttersdk-dusk skill 位于 skills/fluttersdk-dusk/,托管的文档 MCP 位于 mcp.fluttersdk.com;通过 fluttersdk/ai 分发给 8 个以上的 Agent(Claude Code、Cursor、OpenCode、Gemini CLI、VS Code Copilot、Codex CLI、Cline、Roo Code)。这是 Flutter 生态中首个随附自有 LLM Agent skill 包和文档 MCP 的端到端驱动。 |
重要
必须在
lib/main.dart的调用处将DuskPlugin.install()包在if (!kReleaseMode) { ... }中,这样调试版和 profile 版才会包含它(dusk:perf_run会测量 profile 版)。如果此前应用是在kDebugMode下接入的,它在调试版中仍可正常工作,但不会在 profile 版中注册任何ext.dusk.*。只有存在该保护条件时,发布版才会在 Web(dart2js)、桌面(dart2native)和移动端(AOT)摇树优化整个驱动。若没有该保护条件,dusk 会被打包进发布版二进制文件。
AI 编码助手
Dusk 从一开始就以 AI 为先。skills/fluttersdk-dusk/SKILL.md 中的技能会教会你的 Agent 6 条核心法则、e<N> / q<N> 引用语法、6 步可操作性词汇、39 个 MCP 工具,以及 Agent 工作流手册。同一技能也通过 fluttersdk/ai 分发,支持 Claude Code、Cursor、OpenCode、Gemini CLI、VS Code Copilot、Codex CLI、Cline 和 Roo Code,只需一条命令:
npx skills add fluttersdk/ai --skill fluttersdk-dusk
托管在 mcp.fluttersdk.com 的 MCP 服务通过 Streamable HTTP(无需身份验证)提供 search-docs 工具,供需要直接查询 Dusk 文档语料的 Agent 使用。对于仅支持 stdio 的客户端,npx @fluttersdk/mcp 桥接程序会将 stdio 请求代理到上游 HTTP 服务。供大模型读取的目录清单位于 llms.txt。多客户端完整接入说明和 OpenCode 注册表 URL 请参阅 fluttersdk/ai README。
这与 Dusk 自身的运行时 MCP 服务(./bin/fsa mcp:serve,下一节介绍)相互独立:文档 MCP 让 Agent 了解 Dusk;运行时 MCP 则让 Agent 能够观察和操作正在运行的 Flutter 应用。
MCP 安装:8 个客户端
Dusk 是首个专注于 UI 自动化(点击、快照、屏幕截图、观察),而非运行时遥测的 Flutter MCP 服务。这 39 个 dusk_* 工具可供任何兼容 MCP 的 Agent 使用。
说明
以下配置使用
./bin/fsa(fastcli 包装器,参见 README 顶部)。如果尚未搭建 fastcli,请在每个客户端行中将./bin/fsa mcp:serve替换为dart run fluttersdk_dusk mcp:serve。
标准配置(任何会读取项目根目录中 .mcp.json 的客户端):
{
"mcpServers": {
"fluttersdk": {
"command": "./bin/fsa",
"args": ["mcp:serve"],
"cwd": "."
}
}
}
| 客户端 | 安装方式 |
|---|---|
| Claude Code | claude mcp add fluttersdk -- ./bin/fsa mcp:serve |
| VS Code | code --add-mcp '{"name":"fluttersdk","command":"./bin/fsa","args":["mcp:serve"]}',或将标准配置放入 .vscode/mcp.json |
| Cursor | Settings > MCP > Add new MCP Server,或将标准配置放入 ~/.cursor/mcp.json |
| Windsurf | 将标准配置放入 ~/.codeium/windsurf/mcp_config.json |
| Goose | Advanced settings > Extensions > Add custom extension;粘贴标准配置 |
| Warp | Settings > AI > Manage MCP Servers,或使用带标准配置的 /add-mcp 斜杠命令 |
| Claude Desktop | 编辑 claude_desktop_config.json(macOS:~/Library/Application Support/Claude/,Windows:%APPDATA%\Claude\),并在 mcpServers 下添加标准配置 |
| Codex | codex mcp add fluttersdk ./bin/fsa mcp:serve |
使用 Dusk 前后对比
| 不使用 Dusk | 使用 Dusk |
|---|---|
| 你执行热重载,对结果截图,将 PNG 拖入 Claude,输入“计数器按钮应该可以点击,但现在不行,问题出在哪?”,然后滚动、复制相关组件源码,再粘贴回来。 | Claude 调用 dusk_snap 并读取带有 [ref=eN] 标记的 YAML 树。Claude 调用 dusk_tap --ref=e7,触发检查门,计数器递增,Claude 再通过新的快照进行验证。整个循环无需你介入。 |
你不确定测试失败是时序问题还是真正的 bug,于是添加 Future.delayed(Duration(seconds: 2)) 并重新运行。 | 每次手势执行前,都会触发 6 步可操作性检查:对象已失效、未启用、矩形区域为零、位于视口外、不稳定、被其他组件遮挡。错误信息包含可供 Agent 解析的原因字符串。 |
典型 Agent 会话
[agent] artisan_start { device: macos } # launch the app
[agent] dusk_snap {} # semantic tree with stable refs
[agent] dusk_observe {} # structured candidate list (Stagehand-style)
[agent] dusk_tap { ref: "e7" } # tap a button by ref
[agent] dusk_screenshot { format: "jpeg" } # verify visually
[agent] dusk_hot_reload_and_snap {} # reload + snap in one round-trip
[agent] artisan_stop # tear down
每个工具的完整输入架构和调用示例请参阅 MCP 工具参考。对于连接时会读取结构化项目上下文的 Agent,规范入口是 llms.txt;其中列出了 39 个 MCP 工具名称,以及命令列表、引用标记语法和 6 步可操作性词汇。网站向用户提供不带后缀的 URL,并向大模型 Agent 提供相同路径加上 .md 后缀的页面(例如 https://fluttersdk.com/dusk/getting-started.md)。
示例
example/ 目录提供了一个原生 Flutter 展示项目,在同一个路由中为每条 CLI 命令提供实时目标 Widget:TextField、Dropdown、Checkbox、Switch、计数器按钮、Draggable + DragTarget、对话框、底部弹出层、用于滚动的 30 个 ListTile 行,以及跨越三个命名路由的导航按钮。
cd example && flutter pub get
dart run fluttersdk_dusk start --device=macos
文档
完整文档及实时示例请访问 fluttersdk.com/dusk:命令目录、MCP 工具参考、6 步可操作性参考、Magic 和 Wind 集成指南,以及快照增强器编写指南。面向贡献者的内部架构说明位于 ARCHITECTURE.md。
参与贡献
git clone https://github.com/fluttersdk/dusk.git
cd dusk && flutter pub get
flutter test && dart analyze
CI 会在每次推送到 master 以及每个合并到该分支的 PR 上,确保 lib/ 的行覆盖率达到 80% 以上、分析问题为零,并且 lib/、test/ 和 bin/ 没有格式偏差。新增行为应先编写失败测试,再依次经过红、绿、重构。
创建拉取请求前,请运行与 CI 相同的检查:
dart format --output=none --set-exit-if-changed lib/ test/ bin/
dart analyze lib/ test/ bin/
flutter test --exclude-tags=integration --coverage --timeout=30s
dart pub publish --dry-run
许可证
MIT,详情请参阅 LICENSE。
由 FlutterSDK 用心打造
如果 Dusk 帮你节省了调试时间,请点个 Star,这有助于更多人发现它。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。