告别手动调试!用 Flutter MCP 让 AI 直接操控你的 App

直接对 AI 说"帮我登录一下",然后它真的打开你的 Flutter App,填好账号密码,点击登录,跳转到首页。这不是科幻,今天就能用上。


背景:MCP 是什么?

MCP(Model Context Protocol)是 Anthropic 推出的开放协议,让 AI 助手能连接外部工具和服务。简单说,它是 AI 和你的开发工具之间的"翻译官"。

Flutter 官方推出了 dart mcp-server,让 Claude Code 可以直接:

  • 获取运行中 App 的 Widget Tree
  • 执行热重载 / 热重启
  • 查看运行时错误
  • 通过 Flutter Driver 操控 UI

一、环境配置

1. 安装 dart mcp-server

在项目根目录执行:

bash 复制代码
claude mcp add -s project --transport stdio dart -- dart mcp-server

此时项目根目录会自动生成.mcp.json

json 复制代码
{
  "mcpServers": {
    "dart-mcp-server": {
      "command": "dart",
      "args": ["mcp-server"],
      "env": {}
    }
  }
}

2. 开启 VSCode 支持

.vscode/settings.json 中添加:

json 复制代码
{
  "dart.mcpServer": true
}

3. 集成 Flutter Driver

pubspec.yaml 新增 dev 依赖:

yaml 复制代码
dev_dependencies:
  flutter_driver:
    sdk: flutter
  test: any

新建 main_driver.dart 作为 Driver 入口:

dart 复制代码
import 'package:flutter_driver/driver_extension.dart';
import 'lib/main.dart' as app;

void main() {
  enableFlutterDriverExtension(); // 担心生产构建污染,可以用env控制
  app.main();
}

二、连接 MCP

启动应用后,终端会打印 DTD 地址:

arduino 复制代码
ws://127.0.0.1:62633/P6KDlN4NFYw=

把这个地址发给 Claude Code,它会自动连接:

yaml 复制代码
连接成功!Dart MCP 已就绪

连接成功后,Claude 有了"眼睛"和"手"------能看到 Widget Tree,也能操控 UI。

---

## 三、让 AI 直接操控 App

连接成功后,直接用自然语言下指令:

> "执行登录操作,账号 15899999999,密码 a123456,勾选协议,点击登录"

Claude 会自动拆解步骤,依次执行:
  1. waitFor('欢迎登录二手合规通!') → 确认登录页已渲染
  2. tap(phone_input) → 点击手机号输入框
  3. enterText('15899999999') → 输入手机号
  4. tap(password_input) → 点击密码输入框
  5. enterText('a123456') → 输入密码
  6. tap(AppCheckbox) → 勾选协议
  7. tap('登录') → 点击登录按钮
yaml 复制代码
整个过程无需手动操作。

> 关键细节:Driver 要精准定位输入框,需要给 Widget 加上 `Key`:
>
> ```dart
> TextField(
>   key: const Key('phone_input'),
>   ...
> )
> ```

---

## 自动化测试用例

和mcp无关, 使用的是flutter_driver

有了 Flutter Driver 的基础,把操作固化成可重复执行的测试用例。

### 测试文件结构

test/ └── driver/ └── login_test.dart # 登录页测试(8 个用例)

csharp 复制代码
### setUpAll 模板

```dart
setUpAll(() async {
  driver = await FlutterDriver.connect();
  // 等待 runApp() 完成,避免"No root widget"错误
  await driver.waitUntilFirstFrameRasterized();
  // 等待目标页面渲染完成
  await driver.waitFor(find.text('欢迎登录二手合规通!'));
});

不加 waitUntilFirstFrameRasterized() 会报 No root widget is attached,因为 Driver 连接时 runApp() 还没执行完。这是第一个坑。

登录页 8 个测试用例

用例 场景 验证点
TC-01 手机号为空 Toast "请输入手机号"
TC-02 手机号不足 11 位 Toast "登录手机号格式不正确"
TC-03 密码为空 Toast "请输入密码"
TC-04 密码少于 6 位 Toast "请输入6-20位密码"
TC-05 未勾选协议 Toast "请先阅读并同意..."
TC-06 点击清除按钮 输入框内容清空
TC-07 点击协议文字 切换勾选状态
TC-09 正确账号密码+勾选协议 跳转首页(ShellScaffold)

TC-08(点击《用户协议》跳转 WebView)跳过了------WebView 页面会导致 flutter_driver VM service 连接断开,这是已知限制。

核心测试代码片段

dart 复制代码
// TC-06: 清除按钮验证
test('TC-06 手机号清除按钮 - 输入后点击清除,输入框变空', () async {
  await driver.tap(find.byValueKey('phone_input'));
  await driver.enterText('15899999999');

  final textBefore = await driver.getText(find.byValueKey('phone_input'));
  expect(textBefore, '15899999999');

  await driver.tap(find.byValueKey('phone_clear_btn'));

  final textAfter = await driver.getText(find.byValueKey('phone_input'));
  expect(textAfter, '');
});

// TC-09: 正常登录
test('TC-09 正常登录 - 跳转到首页', () async {
  await driver.tap(find.byValueKey('phone_input'));
  await driver.enterText('15899999999');
  await driver.tap(find.byValueKey('password_input'));
  await driver.enterText('a123456');
  await driver.tap(find.byType('AppCheckbox'));
  await driver.tap(find.text('登录'));

  await driver.waitFor(
    find.byType('ShellScaffold'),
    timeout: const Duration(seconds: 10),
  );
});

五、执行测试

运行命令(3 个参数缺一不可)

bash 复制代码
NO_PROXY=127.0.0.1,localhost flutter drive \
  --target=main_driver.dart \
  --driver=test/driver/login_test.dart \
  --dart-define-from-file .env.development
参数 说明
--target=main_driver.dart Driver 入口,固定值
--driver=test/driver/xxx.dart 测试文件路径
--dart-define-from-file 必须,否则 API_HOST 为空导致 App 崩溃
NO_PROXY=127.0.0.1,localhost macOS 有代理时必须设置

测试结果

diff 复制代码
+1: TC-01 手机号为空 - 提示"请输入手机号"
+2: TC-02 手机号格式不正确(少于11位)
+3: TC-03 密码为空 - 提示"请输入密码"
+4: TC-04 密码长度不足(少于6位)
+5: TC-05 未勾选协议 - 提示"请先阅读并同意..."
+6: TC-06 手机号清除按钮 - 输入后点击清除,输入框变空
+7: TC-07 点击协议文字 - 可切换勾选状态
+8: TC-09 正常登录 - 跳转到首页

All tests passed!

TC-09 不只是 UI 测试,它真实调用了后端接口,完整走完了:

复制代码
获取客户端列表 → 登录 → 获取店铺列表 → 跳转首页

六、踩坑总结

调试过程中遇到了不少问题,记录下来省得下次再踩:

问题 原因 解决方案
Connection closed before full header macOS 代理拦截了本地连接 NO_PROXY=127.0.0.1,localhost
Invalid argument (baseUrl): Must be a valid URL 缺少 --dart-define-from-file 补上环境变量参数
No root widget is attached Driver 连接时 runApp() 未完成 waitUntilFirstFrameRasterized()
Service connection disposed WebView 页面导致 VM service 断开 跳过含 WebView 的测试用例
enter_text 无效果 未先 tap 聚焦输入框 tapenterText

七、总结

Flutter MCP 把 AI 变成了你的测试工程师------它能看懂 Widget Tree,能操控 UI,能帮你写测试,还能帮你跑测试。

开发阶段不用反复手动填表单,测试阶段 AI 分析页面结构、补全 Widget Key、生成用例,测试命令固化后可以直接接入 CI 流水线。

写完登录页,直接说一句"帮我测一下登录流程"就够了。


参考文档:Flutter MCP Server 官方文档

相关推荐
浮桥2 小时前
uniapp + h5实现悬浮活动按钮组件
前端·javascript·uni-app
Web_Lys2 小时前
css设置滚动条样式不生效【antDesign UI Table滚动条样式无法自定义 解决方案】
前端·css
a1117762 小时前
星球浏览 漫游(纯html 开源)
前端·开源·html
郝学胜-神的一滴2 小时前
FastAPI:Python 高性能 Web 框架的优雅之选
开发语言·前端·数据结构·python·算法·fastapi
慧一居士2 小时前
vite 使用说明和示例演示
前端
牢七2 小时前
反序列化重点模块 private Object readOrdinaryObject(boolean unshared)废案与反思
java·服务器·前端
NEXT063 小时前
数组转树与树转数组
前端·数据结构·面试
We་ct3 小时前
浏览器 Reflow(重排)与Repaint(重绘)全解析
前端·面试·edge·edge浏览器
笨笨狗吞噬者3 小时前
【uniapp】小程序端解决分包的uni_modules打包后产物进入主包中的问题
前端·微信小程序·uni-app