首页
/ 在 ECC 中使用 /flutter-review 命令对 Flutter/Dart 变更实施工程化代码审查

在 ECC 中使用 /flutter-review 命令对 Flutter/Dart 变更实施工程化代码审查

2026-09-07 18:16:35作者:齐添朝

/flutter-review 是 ECC(Agent Harness Performance Optimization System)面向 Flutter/Dart 工程的内置审查命令:它以 git diff --stagedgit diff 为输入,调用 flutter-reviewer Agent,对 widget 最佳实践、状态管理、Dart 惯用法、性能、可访问性与安全进行系统性检查,并按严重级别输出可执行的修复建议。阅读本文后,你将掌握该命令的触发前置条件、完整审查清单、输出格式与审批门禁规则,并能在提交 PR、发布生产版本前将其嵌入团队的自动化协作流程。

命令定位:一次调用完成的五步审查管线

根据 commands/flutter-review.md 的定义,该命令的核心动作分解为五个步骤,形成了从"收集上下文"到"输出结论"的完整闭环:

  1. Gather Context(收集上下文):审查 git diff --staged(已暂存改动)与 git diff(未暂存改动),确定本次审查的变更集。
  2. Inspect Project(检查项目):读取 pubspec.yamlanalysis_options.yaml,并识别项目采用的状态管理方案(BLoC、Riverpod、Provider、GetX、MobX、Signals 或 Flutter 内置方案)。
  3. Security Pre-scan(安全预扫描):优先检查硬编码密钥、明文 HTTP 调用等 CRITICAL 级安全问题。
  4. Full Review(完整审查):应用完整审查清单进行逐文件、逐行审查。
  5. Report Findings(输出结论):将发现的问题按严重程度分组,并附带修复指引。

从底层实现看,Agent 侧的执行流程与命令描述严格对应。在 agents/flutter-reviewer.md 中,Agent 被要求按顺序执行:先运行 git diff --staged / git diff,若无 diff 则回退到 git log --oneline -5;随后检查 pubspec.yamlanalysis_options.yamlCLAUDE.md,判断是否为 monorepo(melos/workspace),并特别强调识别状态管理与路由/DI 方案后再调整审查口径,避免把惯用法误判为违规。这一设计使整套审查体系是"库无关"的(library-agnostic),无论项目使用哪种状态管理库都能工作。

运行前置条件:让审查建立在可编译的代码之上

在运行 /flutter-review 之前,文档要求依次满足四个前置条件,任何一项未通过都会让审查结论失真:

前置条件 说明 配套命令
构建通过 对损坏代码做审查是不完整的 先运行 /flutter-build
测试通过 确认没有引入回归 先运行 /flutter-test
无合并冲突 冲突会让 diff 混入非本意的改动 手动解决冲突
flutter analyze 干净 静态分析警告应在审查前清零 flutter analyze

这并非孤立要求。在 ECC 的命令生态中,/flutter-build/flutter-review 构成了"先修编译 → 再跑测试 → 最后审查"的流水线关系。commands/flutter-build.md 负责通过 flutter analyzeflutter pub getdart run build_runner build --delete-conflicting-outputs 等诊断命令增量修复构建问题;commands/flutter-test.md 则负责运行 flutter test(支持 --coverage--name--update-goldens 等参数)。审查发生在二者之后,确保评审对象是真实、稳定、可回归的代码。

何时使用该命令

文档给出了五种典型场景:

  • 提交 PR 之前:Flutter/Dart 改动在构建与测试通过后、合入前做最后把关;
  • 新功能实现之后:尽早发现隐患,避免问题累积;
  • 评审他人的 Flutter 代码:复用成熟检查体系,代替零散的人工记忆;
  • 审计特定组件:针对某个 widget、状态管理组件或 service 类做定点体检;
  • 生产版本发布前:作为发布门禁的一部分执行。

审查范围总览:从 CRITICAL 到 LOW 的五级检查体系

命令文档将审查关注点与严重级别映射为一张速览表,它是整套审查的"目录",每一项在 Agent 的详细清单中都有对应的检查细节:

关注领域 严重级别
硬编码密钥、明文 HTTP CRITICAL
架构违规、状态管理反模式 CRITICAL
Widget 重建问题、资源泄漏 HIGH
缺少 dispose()await 之后使用 BuildContext HIGH
Dart 空安全、缺失错误/加载状态 HIGH
const 传播、widget 组合 HIGH
性能:build() 中做昂贵操作 HIGH
可访问性、语义标签 MEDIUM
状态转换缺少测试 HIGH
硬编码字符串(未走 l10n) MEDIUM
Pub 依赖卫生 LOW

安全预扫描与升级机制

审查流程中存在一条重要的中止/升级规则:安全预扫描若发现 CRITICAL 级安全问题,Agent 会立即停止并移交 security-reviewer,而不是继续常规审查。在 agents/flutter-reviewer.md 中,预扫描覆盖六类输入:

  • Dart 源码中硬编码的 API Key、Token 或密钥;
  • 敏感数据明文存储(未使用平台安全存储);
  • 用户输入与 deep link URL 缺少输入校验;
  • 明文 HTTP 流量;经 print()/debugPrint() 泄漏敏感数据;
  • 导出的 Android 组件与 iOS URL scheme 缺少防护。

这一"安全优先、发现即升级"的设计,确保了 CRITICAL 问题不会被淹没在大量中低级别风格建议中,符合 Agent 文档中"将安全、数据丢失与正确性置于风格之上"的优先级原则。

底层 Agent 的噪声控制策略

为了让审查结论真正可用,agents/flutter-reviewer.md 对报告质量做了三条硬约束:

  1. 只报告置信度 > 80% 的问题——避免猜测性结论污染报告;
  2. 同类问题合并——例如"5 个 widget 缺少 const 构造器"合并为一条发现,而不是 5 条噪音;
  3. 默认跳过纯风格偏好——除非违反项目约定或引发功能问题;
  4. 仅对 CRITICAL 安全问题标记未改动代码——控制 diff 之外的信息量。

Agent 的角色定位也被明确为"只报告、不重构"(You DO NOT refactor or rewrite code — you report findings only),与 /flutter-build(负责修改修复)形成职责分离,避免审查者在评审过程中夹带私货改动。

分严重级的详细审查清单解读

CRITICAL:架构与状态管理

架构审查会先识别项目采用的是 Clean Architecture、MVVM 还是 feature-first 结构,再检查层间约束。依据 rules/dart/patterns.md,Clean Architecture 的层边界是:domain 层为纯 Dart(不得 import package:flutter 或任何数据层包)、data 层在仓库边界完成 DTO→领域实体映射、presentation 层调用用例而非直接依赖仓库。审查项因此覆盖:

  • widget 中承载业务逻辑——复杂逻辑应归属状态管理组件而非 build() 或回调;
  • 数据模型跨层泄漏——分离了 DTO 与领域实体的项目必须在边界处映射;
  • 跨层 import——内层不得依赖外层;
  • 框架泄漏进纯 Dart 层——domain/model 层不得 import Flutter;
  • 循环依赖——A 依赖 B 且 B 依赖 A;
  • 跨包私有 src/ import——package:other/src/internal.dart 破坏 Dart 包封装;
  • 业务逻辑中直接实例化依赖——状态管理器应经注入接收依赖;
  • 层边界缺少抽象——应依赖接口而非具体类。

状态管理审查分为两类范式分别执行:不可变状态方案(BLoC、Riverpod、Redux)重点检查状态不可变性(用 copyWith 而非原地修改)与 ==/hashCode 的正确实现;响应式变更方案(MobX、GetX、Signals)重点检查变更是否只经 @action.value.obs 等响应式 API 发生,以及派生值是否使用 computed 机制。两种范式共享以下通识检查:

  • 布尔标志位泛滥isLoading/isError/hasData 独立字段)——使"不可能状态"可被表示;
  • 状态处理非穷尽——未处理的分支会静默失效;
  • 单一职责被破坏——"god" 管理器同时处理无关关注点;
  • widget 直接调用 API/DB——应经 service/repository 层;
  • build() 中订阅——应使用声明式 builder;
  • Stream/订阅泄漏——手动订阅必须在 dispose()/close() 中取消;
  • 缺失 error/loading 状态——每个异步操作都应显式建模 loading/success/error。

针对"穷尽状态"的最佳实践,skills/flutter-dart-code-review/SKILL.md 给出了具体代码示范——用 Dart 3 sealed class 取代布尔标志位,使不可能状态在类型层面不可表示:

// BAD — boolean flag soup allows impossible states
class UserState {
  bool isLoading = false;
  bool hasError = false; // isLoading && hasError is representable!
  User? user;
}

// GOOD (immutable approach) — sealed types make impossible states unrepresentable
sealed class UserState {}
class UserInitial extends UserState {}
class UserLoading extends UserState {}
class UserLoaded extends UserState {
  final User user;
  const UserLoaded(this.user);
}
class UserError extends UserState {
  final String message;
  const UserError(this.message);
}

Agent 还针对跨组件依赖给出按方案区分的审查口径:Riverpod 中 provider 之间 ref.watch 是预期行为,只标记循环或过度缠绕的链;BLoC 中 bloc 不应直接依赖其他 bloc,应优先共享 repository;其余方案遵循各自文档化的通信约定。

HIGH:Widget 组合、性能、资源生命周期与错误处理

Widget 组合检查点包括:build() 超过约 80 行应拆分(SKILL 中放宽到 80–100 行);返回 widget 的私有 _build*() 辅助方法应提取为独立 widget 类(以启用元素复用、const 传播与框架优化);全字段为 final 的 widget 必须声明 const 构造器;StatefulWidget 被滥用(无可变局部状态时应优先 StatelessWidget);列表项缺少稳定 ValueKey;硬编码颜色/文本样式(应使用 Theme.of(context).colorScheme/textTheme,否则破坏深色模式);硬编码间距(应用设计 token 或命名常量)。

性能检查点覆盖:重建范围过宽(状态消费者包裹过大子树,应收窄并使用 selector);build() 中做排序、过滤、正则或 I/O(应在状态层计算);MediaQuery.of(context) 过度使用(应用 MediaQuery.sizeOf(context) 等专用访问器);大数据列表用具体构造器而非 ListView.builder/GridView.builder 惰性构建;图片缺少缓存与 cacheWidth/cacheHeight 解码尺寸控制;动画中直接用 Opacity(应使用 AnimatedOpacity/FadeTransition);IntrinsicHeight/IntrinsicWidth 滥用导致额外布局遍历;复杂独立重绘子树缺少 RepaintBoundary

资源生命周期聚焦 dispose() 义务:initState() 中创建的 controller、订阅、定时器都必须释放;await 之后使用 BuildContext 前必须检查 context.mounted(Flutter 3.7+),文档中给出的修复示例即:

// Fix: Add `if (!context.mounted) return;` before any navigation after awaits (Flutter 3.7+).

此外还包括:异步回调 setState 前检查 mountedBuildContext 不得存储在单例或静态字段中;StreamController 未关闭、Timer 未取消;重复的 init/dispose 生命周期逻辑应提取为可复用模式。

错误处理层面,审查要求同时设置 FlutterError.onErrorPlatformDispatcher.instance.onError 作为全局兜底;接入 Crashlytics/Sentry 等错误上报服务;将状态管理错误观察者(BlocObserver、ProviderObserver 等)接入上报;为 release 模式定制 ErrorWidget.builder 以避免红屏直达用户;原始异常应在到达 UI 前映射为用户友好、已本地化的消息。

HIGH:测试覆盖

测试维度要求状态管理器变更必须有配套单元测试、新增/变更 widget 应有 widget 测试、设计关键组件应有 golden 测试(像素级回归)、所有状态转换路径(loading→success、loading→error、retry、empty)都需覆盖、外部依赖必须 mock 且测试间无共享可变状态、异步测试不得依赖时序假设(使用 pumpAndSettle 或显式 pump(Duration))。这些要求与 rules/dart/testing.md 中的约定互相印证:业务逻辑行覆盖率目标 80%+,flutter test --coverage 的覆盖率不达标应阻断 CI。

MEDIUM:可访问性、平台/响应式与本地化

可访问性rules/dart 与 SKILL 一致):图片缺 semanticLabel、图标缺 tooltip;可交互目标小于 48×48 px;仅用颜色传达状态;装饰性元素未用 ExcludeSemantics、相关元素组未用 MergeSemantics;硬编码字号不响应系统字体缩放;文本对比度低于 4.5:1。

平台、响应式与导航:内容被刘海/状态栏遮挡时缺 SafeArea;Android 返回键或 iOS 边缘右滑返回失效;AndroidManifest.xml/Info.plist 中未声明平台权限;平板/桌面/横屏下固定布局不可用;无界文本未用 Flexible/Expanded/FittedBox 导致溢出;混用 Navigator.push 与声明式路由(应二选一);硬编码路由路径(应使用常量、枚举或生成路由);deep link URL 未净化即导航;受保护路由缺少认证守卫。

国际化(l10n):所有用户可见文本必须走本地化系统;本地化文本禁用字符串拼接(应使用参数化消息);日期、数字、货币格式必须 locale-aware。

LOW:依赖与构建卫生

Pub 依赖检查在命令文档中归为 LOW,但 skills/flutter-dart-code-review/SKILL.md 展开得相当详尽:评估 pub.dev 包时应看 pub points(目标 130+/160)、verified 发布者、最后发布时间(停更超一年有风险)、开源问题响应、许可证兼容性等;版本约束优先用插入符语法 ^1.2.3;定期 flutter pub outdated;生产 pubspec.yaml 中不应有未注释说明的 dependency overrides;monorepo 中内部包只可从公开 API 导入,依赖用 workspace 解析而非硬编码 path: ../../

项目本身应配置严格的静态分析:rules/dart 要求 analysis_options.yaml 启用 strict-castsstrict-inferencestrict-raw-types,采用 very_good_analysisflutter_lints 或自定义严格规则集,且 // ignore: 必须有解释性注释。

实战会话示例:一次完整的登录页审查输出

commands/flutter-review.md 提供了完整的示例会话,直观展示了命令的运行结果形态。以下是其核心内容的还原,帮助你在实际使用前建立对输出格式的预期:

User: /flutter-review

Agent:
# Flutter Code Review

## Context

Changed files:
- lib/features/auth/presentation/login_page.dart
- lib/features/auth/data/auth_repository_impl.dart

State management: Riverpod (detected from pubspec.yaml)
Architecture: feature-first

## Security Pre-scan

✓ No hardcoded secrets detected
✓ No cleartext HTTP calls

## Review Findings

[HIGH] BuildContext used after async gap without mounted check
File: lib/features/auth/presentation/login_page.dart:67
Issue: `context.go('/home')` called after `await auth.login(...)` with no `mounted` check.
Fix: Add `if (!context.mounted) return;` before any navigation after awaits (Flutter 3.7+).

[HIGH] AsyncValue error state not handled
File: lib/features/auth/presentation/login_page.dart:42
Issue: `ref.watch(authProvider)` switches on loading/data but has no `error` branch.
Fix: Add error case to the switch expression or `when()` call to show a user-facing error message.

[MEDIUM] Hardcoded string not localized
File: lib/features/auth/presentation/login_page.dart:89
Issue: `Text('Login')` — user-visible string not using localization system.
Fix: Use the project's l10n accessor: `Text(context.l10n.loginButton)`.

## Review Summary

| Severity | Count | Status |
|----------|-------|--------|
| CRITICAL | 0     | pass   |
| HIGH     | 2     | block  |
| MEDIUM   | 1     | info   |
| LOW      | 0     | note   |

Verdict: BLOCK — HIGH issues must be fixed before merge.

值得注意的是每条发现都遵循 [SEVERITY] 摘要 → File: 路径:行号 → Issue: 问题描述 → Fix: 修复建议 的固定模板,其中包含精确到行号的文件定位,可直接交给开发者或 /flutter-build 执行修复。这一模板同样定义在 Agent 文档的 Output Format 一节中。

输出格式与审批门禁

每一次审查都以统一的汇总表收尾。汇总表的语义如下:

严重级别 计数含义 状态语义
CRITICAL 0 为通过 pass / block
HIGH 影响可否合并 block
MEDIUM 建议优化 info
LOW 风格提示 note

审批规则非常明确:

  • Approve(通过):不存在 CRITICAL 或 HIGH 级问题;
  • Block(阻断):存在任意 CRITICAL 或 HIGH 级问题,必须在合并前修复。

示例中 2 个 HIGH 级问题的结论就是 Verdict: BLOCK。这一"非黑即白"的门禁设计,使 /flutter-review 可以直接作为 PR 合并的自动化闸门使用:HIGH 以上清零即可放行,而 MEDIUM/LOW 仅作信息参考,避免非关键建议阻塞迭代节奏。

库无关的状态管理快速参考

由于审查需要针对项目实际采用的状态管理方案调整口径,SKILL 文档维护了一张跨方案速查表,映射了各方案中"状态容器 / UI 消费者 / Selector / 副作用 / 释放机制 / 测试手段"六个维度的对应物,供审查者与被审查方对齐语言:

原则 BLoC/Cubit Riverpod Provider GetX MobX Signals 内置
状态容器 Bloc/Cubit Notifier/AsyncNotifier ChangeNotifier GetxController Store signal() StatefulWidget
UI 消费者 BlocBuilder ConsumerWidget Consumer Obx/GetBuilder Observer Watch setState
Selector BlocSelector/buildWhen ref.watch(p.select(...)) Selector N/A computed computed() N/A
副作用 BlocListener ref.listen Consumer 回调 ever()/once() reaction effect() callbacks
释放机制 auto via BlocProvider .autoDispose auto via Provider onClose() ReactionDisposer manual dispose()
测试手段 blocTest() ProviderContainer ChangeNotifier 直接测 Get.put in test store 直接测 signal 直接测 widget test

如何与 ECC 中的相关工具协作

/flutter-review 不是孤立命令,它与 ECC 面向 Flutter/Dart 的工具链形成了完整闭环:

在仓库中找到这些文件后,你既可以把 /flutter-review 当作手动触发器在关键节点调用,也可以把它的审批规则(CRITICAL/HIGH 清零)复刻到 CI 门禁中,形成"构建 → 测试 → 审查 → 合并"的自动化质量流水线。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391