Communitygithub.com

flutter-dart-code-review

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

flutter-dart-code-review 是什么?

该技能提供了全面的、与 Flutter/Dart 状态管理库无关的代码审查清单,帮助开发者和 AI 代理在代码审查中识别并修复常见问题。它涵盖了从项目整体健康度、Dart 语言陷阱、Widget 最佳实践,到状态管理架构、性能优化、测试策略、可访问性和安全性等各个方面。该清单适用于任何 Flutter/Dart 项目,无论其使用哪种状态管理方案(如 BLoC、Riverpod、Provider 等)。通过在审查过程中指导用户遵循清单中的每一项,该技能能够系统地确保代码符合行业最佳实践,从而提升代码库的整体质量、可维护性和性能。它旨在作为开发者的得力伙伴,在代码提交或合并前自动触发或手动执行,提供结构化的审查意见。

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

在你喜欢的 AI 中提问

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

文档

Flutter/Dart 代码审查最佳实践

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


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 子类型表示 bug,不应被捕获
  • 未使用的 async:标记为 async 但从未 await 的函数 — 带来不必要的开销
  • 过度使用 late:在可空类型或构造函数初始化更安全的地方使用了 late;将错误推迟到运行时
  • 循环中的字符串拼接:使用 StringBuffer 代替 + 进行迭代字符串构建
  • const 上下文中的可变状态:const 构造函数类中的字段不应是可变的
  • 忽略 Future 返回值:使用 await 或显式调用 unawaited() 来表明意图
  • 使用 var 而 final 也可行:优先对局部变量使用 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 类中(这有助于元素复用、常量传播和框架优化)
  • 在没有可变局部状态时,优先使用无状态 widget 而非有状态 widget
  • 可复用的提取 widget 应放在单独的文件中

常量使用:

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

Key 使用:

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

主题化与设计系统:

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

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、signal 上的 .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 }
// 使用您解决方案的可观察对象/signal 分别包装 status 和 data

重建优化:

  • 状态消费者 widget(Builder、Consumer、Observer、Obx、Watch 等)范围尽可能窄
  • 使用选择器仅在特定字段更改时重建 — 而不是在每次状态发出时
  • 使用 const widget 阻止重建在 widget 树中传播
  • 计算/派生状态是响应式计算的,而不是冗余存储的

订阅与释放:

  • 所有手动订阅 (.listen()) 在 dispose() / close() 中被取消
  • 不再需要时关闭 Stream 控制器
  • 定时器在释放生命周期中被取消
  • 框架管理的生命周期优先于手动订阅(声明式 builder 优于 .listen())
  • 异步回调中在 setState 之前进行 mounted 检查
  • 在 await 之后,未检查 context.mounted 前不要使用 BuildContext(Flutter 3.7+)— 过期的 context 会导致崩溃
  • 在异步间隙后,未验证 widget 是否仍然挂载,不要执行导航、弹出对话框或显示 Scaffold 消息
  • BuildContext 绝不应存储在单例、状态管理器或静态字段中

局部状态与全局状态:

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

5. 性能

不必要的重建:

  • setState() 不在根 widget 级别调用 — 局部化状态更改
  • 使用 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 客户端、数据库、服务)被模拟或伪造
  • 每个测试文件只测试一个类/单元
  • 测试验证行为,而不是实现细节
  • 存根仅定义每个测试所需的行为(最小存根)
  • 测试用例之间没有共享的可变状态

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. 安全

安全存储:

  • 敏感数据...

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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

相关技能