ホーム/テスト&QA/flutter-dart-code-review
F

flutter-dart-code-review

by @affaan-mv
4.4(20)

プロジェクト構造とコード品質の一貫性を確保するための、包括的でライブラリに依存しないFlutter/Dartアプリケーションレビューチェックリスト。

testingflutterdartcode-reviewquality-assuranceGitHub
インストール方法
npx skills add https://github.com/affaan-m/everything-claude-code --skill flutter-dart-code-review
compare_arrows

Before / After 効果比較

1 组
使用前

経験に基づいてFlutterコードをレビューすると、状態管理、ライフサイクル、パフォーマンス最適化などの重要な問題を見落としがちです。レビューカバレッジは約60%です。

使用後

構造化されたチェックリストに従って項目ごとに確認し、アーキテクチャ、パフォーマンス、セキュリティなど12の側面を網羅します。レビューカバレッジは95%に達します。

SKILL.md

flutter-dart-code-review

Flutter/Dart Code Review Best Practices

Comprehensive, library-agnostic checklist for reviewing Flutter/Dart applications. These principles apply regardless of which state management solution, routing library, or DI framework is used.

1. General Project Health

  • Project follows consistent folder structure (feature-first or layer-first)

  • Proper separation of concerns: UI, business logic, data layers

  • No business logic in widgets; widgets are purely presentational

  • pubspec.yaml is clean — no unused dependencies, versions pinned appropriately

  • analysis_options.yaml includes a strict lint set with strict analyzer settings enabled

  • No print() statements in production code — use dart:developer log() or a logging package

  • Generated files (.g.dart, .freezed.dart, .gr.dart) are up-to-date or in .gitignore

  • Platform-specific code isolated behind abstractions

2. Dart Language Pitfalls

  • Implicit dynamic: Missing type annotations leading to dynamic — enable strict-casts, strict-inference, strict-raw-types

  • Null safety misuse: Excessive ! (bang operator) instead of proper null checks or Dart 3 pattern matching (if (value case var v?))

  • Type promotion failures: Using this.field where local variable promotion would work

  • Catching too broadly: catch (e) without on clause; always specify exception types

  • Catching Error: Error subtypes indicate bugs and should not be caught

  • Unused async: Functions marked async that never await — unnecessary overhead

  • late overuse: late used where nullable or constructor initialization would be safer; defers errors to runtime

  • String concatenation in loops: Use StringBuffer instead of + for iterative string building

  • Mutable state in const contexts: Fields in const constructor classes should not be mutable

  • Ignoring Future return values: Use await or explicitly call unawaited() to signal intent

  • var where final works: Prefer final for locals and const for compile-time constants

  • Relative imports: Use package: imports for consistency

  • Mutable collections exposed: Public APIs should return unmodifiable views, not raw List/Map

  • Missing Dart 3 pattern matching: Prefer switch expressions and if-case over verbose is checks and manual casting

  • Throwaway classes for multiple returns: Use Dart 3 records (String, int) instead of single-use DTOs

  • print() in production code: Use dart:developer log() or the project's logging package; print() has no log levels and cannot be filtered

3. Widget Best Practices

Widget decomposition:

  • No single widget with a build() method exceeding ~80-100 lines

  • Widgets split by encapsulation AND by how they change (rebuild boundaries)

  • Private _build*() helper methods that return widgets are extracted to separate widget classes (enables element reuse, const propagation, and framework optimizations)

  • Stateless widgets preferred over Stateful where no mutable local state is needed

  • Extracted widgets are in separate files when reusable

Const usage:

  • const constructors used wherever possible — prevents unnecessary rebuilds

  • const literals for collections that don't change (const [], const {})

  • Constructor is declared const when all fields are final

Key usage:

  • ValueKey used in lists/grids to preserve state across reorders

  • GlobalKey used sparingly — only when accessing state across the tree is truly needed

  • UniqueKey avoided in build() — it forces rebuild every frame

  • ObjectKey used when identity is based on a data object rather than a single value

Theming & design system:

  • Colors come from Theme.of(context).colorScheme — no hardcoded Colors.red or hex values

  • Text styles come from Theme.of(context).textTheme — no inline TextStyle with raw font sizes

  • Dark mode compatibility verified — no assumptions about light background

  • Spacing and sizing use consistent design tokens or constants, not magic numbers

Build method complexity:

  • No network calls, file I/O, or heavy computation in build()

  • No Future.then() or async work in build()

  • No subscription creation (.listen()) in build()

  • setState() localized to smallest possible subtree

4. State Management (Library-Agnostic)

These principles apply to all Flutter state management solutions (BLoC, Riverpod, Provider, GetX, MobX, Signals, ValueNotifier, etc.).

Architecture:

  • Business logic lives outside the widget layer — in a state management component (BLoC, Notifier, Controller, Store, ViewModel, etc.)

  • State managers receive dependencies via injection, not by constructing them internally

  • A service or repository layer abstracts data sources — widgets and state managers should not call APIs or databases directly

  • State managers have a single responsibility — no "god" managers handling unrelated concerns

  • Cross-component dependencies follow the solution's conventions:

In Riverpod: providers depending on providers via ref.watch is expected — flag only circular or overly tangled chains

  • In BLoC: blocs should not directly depend on other blocs — prefer shared repositories or presentation-layer coordination

  • In other solutions: follow the documented conventions for inter-component communication

Immutability & value equality (for immutable-state solutions: BLoC, Riverpod, Redux):

  • State objects are immutable — new instances created via copyWith() or constructors, never mutated in-place

  • State classes implement == and hashCode properly (all fields included in comparison)

  • Mechanism is consistent across the project — manual override, Equatable, freezed, Dart records, or other

  • Collections inside state objects are not exposed as raw mutable List/Map

Reactivity discipline (for reactive-mutation solutions: MobX, GetX, Signals):

  • State is only mutated through the solution's reactive API (@action in MobX, .value on signals, .obs in GetX) — direct field mutation bypasses change tracking

  • Derived values use the solution's computed mechanism rather than being stored redundantly

  • Reactions and disposers are properly cleaned up (ReactionDisposer in MobX, effect cleanup in Signals)

State shape design:

  • Mutually exclusive states use sealed types, union variants, or the solution's built-in async state type (e.g. Riverpod's AsyncValue) — not boolean flags (isLoading, isError, hasData)

  • Every async operation models loading, success, and error as distinct states

  • All state variants are handled exhaustively in UI — no silently ignored cases

  • Error states carry error information for display; loading states don't carry stale data

  • Nullable data is not used as a loading indicator — states are explicit

// 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);
}

// GOOD (reactive approach) — observable enum + data, mutations via reactivity API
// enum UserStatus { initial, loading, loaded, error }
// Use your solution's observable/signal to wrap status and data separately

Rebuild optimization:

  • State consumer widgets (Builder, Consumer, Observer, Obx, Watch, etc.) scoped as narrow as possible

  • Selectors used to rebuild only when specific fields change — not on every state emission

  • const widgets used to stop rebuild propagation through the tree

  • Computed/derived state is calculated reactively, not stored redundantly

Subscriptions & disposal:

  • All manual subscriptions (.listen()) are cancelled in dispose() / close()

  • Stream controllers are closed when no longer needed

  • Timers are cancelled in disposal lifecycle

  • Framework-managed lifecycle is preferred over manual subscription (declarative builders over .listen())

  • mounted check before setState in async callbacks

  • BuildContext not used after await without checking context.mounted (Flutter 3.7+) — stale context causes crashes

  • No navigation, dialogs, or scaffold messages after async gaps without verifying the widget is still mounted

  • BuildContext never stored in singletons, state managers, or static fields

Local vs global state:

  • Ephemeral UI state (checkbox, slider, animation) uses local state (setState, ValueNotifier)

  • Shared state is lifted only as high as needed — not over-globalized

  • Feature-scoped state is properly disposed when the feature is no longer active

5. Performance

Unnecessary rebuilds:

  • setState() not called at root widget level — localize state changes

  • const widgets used to stop rebuild propagation

  • RepaintBoundary used around complex subtrees that repaint independently

  • AnimatedBuilder child parameter used for subtrees independent of animation

Expensive operations in build():

  • No sorting, filtering, or mapping large collections in build() — compute in state management layer

  • No regex compilation in build()

  • MediaQuery.of(context) usage is specific (e.g., MediaQuery.sizeOf(context))

Image optimization:

  • Network images use caching (any caching solution appropriate for the project)

  • Appropriate image resolution for target device (no loading 4K images for thumbnails)

  • Image.asset with cacheWidth/cacheHeight to decode at display size

  • Placeholder and error widgets provided for network images

Lazy loading:

  • ListView.builder / GridView.builder used instead of ListView(children: [...]) for large or dynamic lists (concrete constructors are fine for small, static lists)

  • Pagination implemented for large data sets

  • Deferred loading (deferred as) used for heavy libraries in web builds

Other:

  • Opacity widget avoided in animations — use AnimatedOpacity or FadeTransition

  • Clipping avoided in animations — pre-clip images

  • operator == not overridden on widgets — use const constructors instead

  • Intrinsic dimension widgets (IntrinsicHeight, IntrinsicWidth) used sparingly (extra layout pass)

6. Testing

Test types and expectations:

  • Unit tests: Cover all business logic (state managers, repositories, utility functions)

  • Widget tests: Cover individual widget behavior, interactions, and visual output

  • Integration tests: Cover critical user flows end-to-end

  • Golden tests: Pixel-perfect comparisons for design-critical UI components

Coverage targets:

  • Aim for 80%+ line coverage on business logic

  • All state transitions have corresponding tests (loading → success, loading → error, retry, etc.)

  • Edge cases tested: empty states, error states, loading states, boundary values

Test isolation:

  • External dependencies (API clients, databases, services) are mocked or faked

  • Each test file tests exactly one class/unit

  • Tests verify behavior, not implementation details

  • Stubs define only the behavior needed for each test (minimal stubbing)

  • No shared mutable state between test cases

Widget test quality:

  • pumpWidget and pump used correctly for async operations

  • find.byType, find.text, find.byKey used appropriately

  • No flaky tests depending on timing — use pumpAndSettle or explicit pump(Duration)

  • Tests run in CI and failures block merges

7. Accessibility

Semantic widgets:

  • Semantics widget used to provide screen reader labels where automatic labels are insufficient

  • ExcludeSemantics used for purely decorative elements

  • MergeSemantics used to combine related widgets into a single accessible element

  • Images have semanticLabel property set

Screen reader support:

  • All interactive elements are focusable and have meaningful descriptions

  • Focus order is logical (follows visual reading order)

Visual accessibility:

  • Contrast ratio >= 4.5:1 for text against background

  • Tappable targets are at least 48x48 pixels

  • Color is not the sole indicator of state (use icons/text alongside)

  • Text scales with system font size settings

Interaction accessibility:

  • No no-op onPressed callbacks — every button does something or is disabled

  • Error fields suggest corrections

  • Context does not change unexpectedly while user is inputting data

8. Platform-Specific Concerns

iOS/Android differences:

  • Platform-adaptive widgets used where appropriate

  • Back navigation handled correctly (Android back button, iOS swipe-to-go-back)

  • Status bar and safe area handled via SafeArea widget

  • Platform-specific permissions declared in AndroidManifest.xml and Info.plist

Responsive design:

  • LayoutBuilder or MediaQuery used for responsive layouts

  • Breakpoints defined consistently (phone, tablet, desktop)

  • Text doesn't overflow on small screens — use Flexible, Expanded, FittedBox

  • Landscape orientation tested or explicitly locked

  • Web-specific: mouse/keyboard interactions supported, hover states present

9. Security

Secure storage:

  • Sensitive data (tokens, credentials) stored using platform-secure storage (Keychain on iOS, EncryptedSharedPreferences on Android)

  • Never store secrets in plaintext storage

  • Biometric authentication gating considered for sensitive operations

API key handling:

  • API keys NOT hardcoded in Dart source — use --dart-define, .env files excluded from VCS, or compile-time configuration

  • Secrets not committed to git — check .gitignore

  • Backend proxy used for truly secret keys (client should never hold server secrets)

Input validation:

  • All user input validated before sending to API

  • Form validation uses proper validation patterns

  • No raw SQL or string interpolation of user input

  • Deep link URLs validated and sanitized before navigation

Network security:

  • HTTPS enforced for all API calls

  • Certificate pinning considered for high-security apps

  • Authentication tokens refreshed and expired properly

  • No sensitive data logged or printed

10. Package/Dependency Review

Evaluating pub.dev packages:

  • Check pub points score (aim for 130+/160)

  • Check likes and popularity as community signals

  • Verify the publisher is verified on pub.dev

  • Check last publish date — stale packages (>1 year) are a risk

  • Review open issues and response time from m

...

ユーザーレビュー (0)

レビューを書く

効果
使いやすさ
ドキュメント
互換性

レビューなし

統計データ

インストール数5.4K
評価4.4 / 5.0
バージョン
更新日2026年8月6日
比較事例1 件

ユーザー評価

4.4(20)
5
35%
4
50%
3
15%
2
0%
1
0%

この Skill を評価

0.0

対応プラットフォーム

🔧Claude Code

タイムライン

作成2026年3月27日
最終更新2026年8月6日
🎁 Agent 知識カード
アンケート