Communitygithub.com

Flutter/Dart 程式碼審查最佳實務

一套與函式庫無關的 Flutter/Dart 程式碼審查檢查清單,涵蓋 Widget 最佳實務、狀態管理模式(BLoC、Riverpod、Provider、GetX、MobX、Signals)、Dart 慣用寫法、效能、無障礙、安全性及乾淨架構。適合在審查任何使用這些技術的 Flutter 或 Dart 專案時使用。

Dart 程式碼審查最佳實務 是什麼?

這項技能提供一份全面且與函式庫無關的檢查清單,專門用於審查 Flutter 和 Dart 應用程式。它涵蓋專案整體健康狀態、Dart 語言陷阱、Widget 最佳實務、狀態管理(無論使用 BLoC、Riverpod、Provider、GetX、MobX 或 Signals)、效能最佳化、測試策略、無障礙性、平台特定考量及安全性。此技能的核心目標是建立一致的程式碼審查標準,確保遵循乾淨架構、不可變性、適當的狀態機設計及重構最佳化。支援所有主流 Flutter 狀態管理解決方案,並強調在 UI 層與業務邏輯之間實現清晰的關注點分離。此檢查清單適用於任何 Flutter/Dart 專案的程式碼審查流程。

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

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

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 子類型表示程式碼錯誤,不應被捕獲
  • 未使用的 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 類別(啟用元素重用、const 傳播及框架最佳化)
  • 在不需要可變區域狀態時,偏好 Stateless widget 而非 Stateful widget
  • 可重用的提取 widget 放在獨立的檔案中

Const 使用:

  • 盡可能使用 const 建構子 — 避免不必要的重建
  • 不會變更的集合使用 const 字面值(const []、const {})
  • 當所有欄位都是 final 時,建構子宣告為 const

Key 使用:

  • 在列表/網格中使用 ValueKey 以在重新排序時保留狀態
  • 謹慎使用 GlobalKey — 僅在確實需要跨樹存取狀態時使用
  • 避免在 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 records 或其他
  • 狀態物件內的集合不會以原始可變的 List/Map 形式暴露

反應式原則(適用於反應式變動的解決方案:MobX、GetX、Signals):

  • 狀態僅透過解決方案的反應式 API(MobX 的 @action、Signals 的 .value、GetX 的 .obs)進行變動 — 直接欄位賦值會繞過變更追蹤
  • 衍生值使用解決方案的計算機制,而非冗餘儲存
  • 反應和清除器已正確清理(MobX 的 ReactionDisposer、Signals 的效果清理)

狀態形狀設計:

  • 互斥的狀態使用密封類型、聯合變體或解決方案內建的異步狀態類型(例如 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);
}

// 好的做法(反應式方法) — 可觀察的 enum + 資料,透過反應式 API 進行變動
// enum UserStatus { initial, loading, loaded, error }
// 使用解決方案的 observable/signal 分別包裝狀態和資料

重建最佳化:

  • 狀態消費者 widget(Builder、Consumer、Observer、Obx、Watch 等)的範圍盡可能窄
  • 使用選擇器僅在特定欄位變更時重建 — 不是在每個狀態發射時
  • 使用 const widget 來阻止重建在樹中傳播
  • 計算/衍生狀態以反應式方式計算,而非冗餘儲存

訂閱與釋放:

  • 所有手動訂閱(.listen())在 dispose() / close() 中取消
  • Stream controller 在不再需要時關閉
  • 定時器在釋放生命週期中取消
  • 偏好使用框架管理的生命週期而非手動訂閱(宣告式 builder 勝過 .listen())
  • 在異步回呼中呼叫 setState 之前檢查 mounted
  • 在未檢查 context.mounted(Flutter 3.7+)的情況下,不要在 await 之後使用 BuildContext — 過時的 context 會導致崩潰
  • 在未驗證 widget 仍然掛載的情況下,不要在異步間隔後進行導航、顯示對話框或訊息
  • 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 客戶端、資料庫、服務)被模擬或偽造
  • 每個測試檔僅測試一個類別/單元
  • 測試驗證行為,而非實作細節
  • 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. 安全性

安全儲存:

  • 敏感資料

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.

相關技能