Communitygithub.com

Flutter/Dart 代码审查最佳实践

库无关的 Flutter/Dart 代码审查清单,涵盖 Widget 最佳实践、状态管理模式(BLoC、Riverpod、Provider、GetX、MobX、Signals)、Dart 惯用法、性能、可访问性、安全和整洁架构。适用于审查任何状态管理库的 Flutter 或 Dart 代码。

Dart 代码审查最佳实践 是什么?

这是一个全面的 Flutter/Dart 代码审查指南,旨在帮助开发者系统性地检查代码质量。该技能支持所有常见的 Flutter 状态管理解决方案(BLoC、Riverpod、Provider、GetX、MobX、Signals 等),并提供库无关的最佳实践。 它的核心价值在于将零散的经验总结为可执行的检查项清单,涵盖从项目结构、Dart 语言陷阱到 Widget 分解、状态设计、性能优化和测试等 10 个关键领域。例如,在状态管理中推荐使用密封类替代布尔标志位来避免非法状态组合,在 Widget 构建中强调优先使用 `const` 构造函数和窄范围的状态消费者来优化性能。 该技能适用于 Cursor、Claude Code、Codex 等 AI 代码助手,可作为代码审查的标准提示词。使用方法是将待审查的代码与清单中的条目逐一对比,标记出违反最佳实践的地方,并给出改进建议。特别适合大型项目或团队协作中保持代码风格和架构一致性。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/flutter-dart-code-review

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Dart 代码审查最佳实践 是做什么的?

一份全面且库无关的清单,用于审查 Flutter/Dart 应用程序。这些原则适用于任何状态管理解决方案、路由库或 DI 框架。


1. 项目整体健康度

  • 项目遵循一致的文件夹结构(按功能或按层级)
  • 职责分离合理:UI、业务逻辑、数据层
  • Widget 中不包含业务逻辑;Widget 纯粹用于呈现
  • pubspec.yaml 整洁——无未使用的依赖项,版本已适当锁定
  • analysis_options.yaml 包含严格的 lint 规则集,并启用了严格的分析器设置
  • 生产代码中无 print() 语句——使用 dart:developer 的 log() 或日志记录包
  • 生成的文件(.g.dart、.freezed.dart、.gr.dart)保持最新,或已加入 .gitignore
  • 平台特定代码已通过抽象隔离

2. Dart 语言陷阱

  • 隐式 dynamic:缺少类型注解导致 dynamic——启用 strict-casts、strict-inference、strict-raw-types
  • 空安全误用:过度使用 !(非空断言运算符)而不用正确的空检查或 Dart 3 模式匹配(if (value case var v?))
  • 类型提升失败:使用 this.field 而本地变量提升已可工作
  • 异常捕获过于宽泛:catch (e) 未使用 on 子句;始终指定异常类型
  • 捕获 Error:Error 子类型表示程序错误,不应被捕获
  • 未使用的 async:标记为 async 的函数从未使用 await——带来不必要的开销
  • 过度使用 late:在可使用可空类型或构造函数初始化的场景使用 late,会将错误推迟到运行时
  • 循环中的字符串拼接:应使用 StringBuffer 代替 + 进行迭代字符串构建
  • const 上下文中的可变状态:const 构造函数类中的字段不应是可变的
  • 忽略 Future 返回值:使用 await 或显式调用 unawaited() 以表明意图
  • 能用 final 却用 var:优先使用 final 声明局部变量,使用 const 声明编译时常量
  • 相对导入:为保持一致,应使用 package: 导入
  • 暴露可变集合:公共 API 应返回不可修改的视图,而非原始的 List/Map
  • 缺少 Dart 3 模式匹配:优先使用 switch 表达式和 if-case,而非冗长的 is 检查和手动类型转换
  • 为多个返回值创建一次性类:使用 Dart 3 的记录 (String, int) 而非一次性 DTO
  • 生产代码中的 print():使用 dart:developer 的 log() 或项目的日志包;print() 没有日志级别,无法过滤

3. Widget 最佳实践

Widget 分解:

  • 单个 Widget 的 build() 方法不应超过约 80-100 行
  • Widget 应同时根据封装性和变化方式(重建边界)进行拆分
  • 返回 Widget 的私有 _build*() 辅助方法应抽取为单独的 Widget 类(以启用元素复用、const 传播和框架优化)
  • 在不需要可变本地状态时,优先使用 StatelessWidget 而非 StatefulWidget
  • 可复用的抽取 Widget 应放在单独的文件中

Const 使用:

  • 尽可能使用 const 构造函数——防止不必要的重建
  • 对于不变的集合,使用 const 字面量(const []、const {})
  • 当所有字段都是 final 时,构造函数应声明为 const

Key 使用:

  • 在列表/网格中使用 ValueKey 以在重新排序时保持状态
  • 谨慎使用 GlobalKey——仅在确实需要跨 Widget 树访问状态时使用
  • 避免在 build() 中使用 UniqueKey——它会强制每帧重建
  • 当标识基于数据对象而非单个值时,使用 ObjectKey

主题与设计系统:

  • 颜色来自 Theme.of(context).colorScheme——不使用硬编码的 Colors.red 或十六进制值
  • 文本样式来自 Theme.of(context).textTheme——不使用内联的 TextStyle 和原始字体大小
  • 已验证深色模式兼容性——不假设背景为浅色
  • 间距和尺寸使用一致的设计 Token 或常量,而非魔法数字

Build 方法复杂度:

  • build() 中不执行网络调用、文件 I/O 或重型计算
  • build() 中不执行 Future.then() 或 async 工作
  • build() 中不创建订阅(.listen())
  • setState() 限定在尽可能小的子树范围内

4. 状态管理(库无关)

这些原则适用于所有 Flutter 状态管理解决方案(BLoC、Riverpod、Provider、GetX、MobX、Signals、ValueNotifier 等)。

架构:

  • 业务逻辑位于 Widget 层之外——在状态管理组件中(BLoC、Notifier、Controller、Store、ViewModel 等)
  • 状态管理器通过依赖注入接收依赖项,而非内部构建
  • 服务层或仓库层抽象数据源——Widget 和状态管理器不应直接调用 API 或数据库
  • 状态管理器具有单一职责——不存在处理无关事务的“上帝”管理器
  • 跨组件依赖遵循该解决方案的约定:
    • 在 Riverpod 中:provider 通过 ref.watch 依赖其他 provider 是预期的——仅标记循环或过度纠缠的链
    • 在 BLoC 中:bloc 不应直接依赖其他 bloc——优先使用共享仓库或呈现层协调
    • 在其他解决方案中:遵循组件间通信的文档约定

不可变性与值相等性(针对不可变状态解决方案:BLoC、Riverpod、Redux):

  • 状态对象是不可变的——通过 copyWith() 或构造函数创建新实例,绝不在原位置修改
  • 状态类正确实现了 == 和 hashCode(所有字段包含在比较中)
  • 机制在项目中保持一致——手动重写、Equatable、freezed、Dart 记录或其他
  • 状态对象内的集合不会以原始可变 List/Map 形式暴露

响应式纪律(针对响应式变更解决方案:MobX、GetX、Signals):

  • 状态仅通过该解决方案的响应式 API(MobX 中的 @action、信号上的 .value、GetX 中的 .obs)进行变更——直接字段修改会绕过变更追踪
  • 派生值使用该解决方案的计算机制,而非冗余存储
  • 反应和释放器正确清理(MobX 中的 ReactionDisposer、Signals 中的 effect 清理)

状态形状设计:

  • 互斥状态使用密封类型、联合变体或解决方案内置的异步状态类型(例如 Riverpod 的 AsyncValue)——而非布尔标志位(isLoading、isError、hasData)
  • 每个异步操作将加载、成功和错误建模为不同的状态
  • UI 中穷尽处理所有状态变体——无静默忽略的情况
  • 错误状态携带错误信息以供显示;加载状态不携带过时数据
  • 可空数据不用作加载指示器——状态应明确表示
// 错误——布尔标志位组合允许非法状态
class UserState {
  bool isLoading = false;
  bool hasError = false; // isLoading && hasError 是可表示的!
  User? user;
}

// 正确(不可变方式)——密封类型使非法状态不可表示
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);
}

// 正确(响应式方式)——可观察枚举 + 数据,通过响应式 API 进行变更
// enum UserStatus { initial, loading, loaded, error }
// 使用解决方案的可观察量/信号将状态和数据分开包装

重建优化:

  • 状态消费者 Widget(Builder、Consumer、Observer、Obx、Watch 等)的作用域尽可能窄
  • 使用选择器仅在特定字段更改时重建——而非每次状态发射都重建
  • 使用 const Widget 阻止重建沿 Widget 树传播
  • 计算/派生状态通过响应式计算,而非冗余存储

订阅与释放:

  • 所有手动订阅(.listen())在 dispose() / close() 中取消
  • 不再需要时关闭 StreamController
  • 定时器在释放生命周期中取消
  • 优先使用框架管理的生命周期而非手动订阅(声明式构建器优于 .listen())
  • 在异步回调中 setState 前检查 mounted
  • 在 await 后不使用 BuildContext,除非检查了 context.mounted(Flutter 3.7+)——过时的上下文会导致崩溃
  • 在异步间隙后,未验证 Widget 仍挂载前,不执行导航、对话框或 SnackBar 消息
  • BuildContext 绝不存储在单例、状态管理器或静态字段中

局部状态与全局状态:

  • 临时 UI 状态(复选框、滑块、动画)使用局部状态(setState、ValueNotifier)
  • 共享状态仅提升到必要的高度——不过度全局化
  • 功能范围的状态在功能不再活跃时正确释放

5. 性能

不必要的重建:

  • 不在根 Widget 级别调用 setState() ——将状态更改限定在局部
  • 使用 const Widget 阻止重建传播
  • 在独立重绘的复杂子树周围使用 RepaintBoundary
  • 对于独立于动画的子树,使用 AnimatedBuilder 的 child 参数

Build() 中的昂贵操作:

  • build() 中不执行大型集合的排序、过滤或映射——在状态管理层进行计算
  • build() 中不进行正则表达式编译
  • MediaQuery.of(context) 的使用要具体(例如,MediaQuery.sizeOf(context))

图片优化:

  • 网络图片使用缓存(任何适合项目的缓存方案)
  • 为目标设备使用合适的分辨率(不要为缩略图加载 4K 图片)
  • 使用 Image.asset 配合 cacheWidth/cacheHeight 以显示尺寸解码
  • 为网络图片提供占位符和错误 Widget

懒加载:

  • 对于大型或动态列表,使用 ListView.builder / GridView.builder 而非 ListView(children: [...])(小型静态列表可以使用具体构造函数)
  • 大型数据集实现分页
  • Web 构建中为重型库使用延迟加载(deferred as)

其他:

  • 动画中避免使用 Opacity Widget——使用 AnimatedOpacity 或 FadeTransition
  • 动画中避免裁剪——预裁剪图片
  • 不在 Widget 上重写 operator == ——使用 const 构造函数代替
  • 谨慎使用内在尺寸 Widget(IntrinsicHeight、IntrinsicWidth)(会额外增加布局计算)

6. 测试

测试类型与期望:

  • 单元测试:覆盖所有业务逻辑(状态管理器、仓库、工具函数)
  • Widget 测试:覆盖单个 Widget 的行为、交互和视觉输出
  • 集成测试:端到端覆盖关键用户流程
  • 快照测试:对设计关键的 UI 组件进行像素级精确对比

覆盖率目标:

  • 业务逻辑行覆盖率力争达到 80% 以上
  • 所有状态转换都有对应的测试(加载→成功、加载→错误、重试等)
  • 测试边缘情况:空状态、错误状态、加载状态、边界值

测试隔离:

  • 外部依赖(API 客户端、数据库、服务)被模拟或伪造
  • 每个测试文件仅测试一个类/单元
  • 测试验证的是行为,而非实现细节
  • Stub 仅定义每个测试所需的行为(最小化 Stub)
  • 测试用例之间不共享可变状态

Widget 测试质量:

  • 正确使用 pumpWidget 和 pump 处理异步操作
  • 正确使用 find.byType、find.text、find.byKey
  • 没有依赖时机的不可靠测试——使用 pumpAndSettle 或显式的 pump(Duration)
  • 测试在 CI 中运行,失败会阻止合并

7. 可访问性

语义 Widget:

  • 在自动标签不足时,使用 Semantics Widget 提供屏幕阅读器标签
  • 为纯装饰性元素使用 ExcludeSemantics
  • 使用 MergeSemantics 将相关 Widget 合并为单个可访问元素
  • 图片设置了 semanticLabel 属性

屏幕阅读器支持:

  • 所有交互元素都可聚焦,并具有有意义的描述
  • 焦点顺序符合逻辑(遵循视觉阅读顺序)

视觉可访问性:

  • 文本与背景的对比度比率 >= 4.5:1
  • 可点击目标至少为 48x48 像素
  • 颜色不是状态的唯一指示符(同时使用图标/文本)
  • 文本随系统字体大小设置缩放

交互可访问性:

  • 无空操作 onPressed 回调——每个按钮都有具体操作或被禁用
  • 错误字段建议更正
  • 用户输入数据时,上下文不会意外变化

8. 平台特定问题

iOS/Android 差异:

  • 在适当位置使用平台自适应 Widget
  • 正确处理返回导航(Android 返回按钮、iOS 滑动返回)
  • 通过 SafeArea Widget 处理状态栏和安全区域
  • 平台特定权限已在 AndroidManifest.xml 和 Info.plist 中声明

响应式设计:

  • 使用 LayoutBuilder 或 MediaQuery 实现响应式布局
  • 断点定义一致(手机、平板、桌面)
  • 文本在小屏幕上不溢出——使用 Flexible、Expanded、FittedBox
  • 横屏模式已测试或已明确锁定
  • Web 特定:支持鼠标/键盘交互,存在悬停状态

9. 安全

安全存储:

  • 敏感数据存储在安全存储中(如 flutter_secure_storage),而非 SharedPreferences 或明文文件
  • 令牌和密钥不在代码中硬编码——使用环境变量或安全配置服务
  • 本地缓存中的敏感数据在注销时被清除

网络通信:

  • 所有 API 调用使用 HTTPS
  • 证书固定已考虑用于高安全性场景
  • API 密钥或令牌不在日志或错误报告中泄露
  • 敏感端点的请求/响应未在调试工具外记录

输入验证:

  • 表单输入已针对类型、长度和格式进行验证
  • 验证规则在客户端和服务器端都适用
  • 用户输入在显示前被转义,以防注入攻击

10. 整洁架构与依赖注入

依赖注入:

  • 依赖通过 DI 容器或构造函数注入传递——不在类内部实例化
  • 生命周期管理适当(单例、作用域实例、瞬态)
  • 测试中的模拟通过 DI 轻松替换,无需修改生产代码
  • DI 配置集中且清晰

整洁架构层级:

  • 演示层(Widget、页面)不包含领域实体或数据模型
  • 领域层是纯粹的 Dart,无框架依赖——包含实体、用例和仓库接口
  • 数据层实现仓库接口,处理 JSON 序列化/反序列化
  • 层之间的依赖指向内部(领域层不依赖于数据层)
  • 数据传输对象(DTO)/模型不会泄露到表示层——它们被映射到领域实体

这份清单来自 Flutter/Dart 代码审查最佳实践。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能