AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Flutter Dart Code Review

skill-affaan-m-ecc-flutter-dart-code-review · by affaan-m

Library-agnostic Flutter/Dart code review checklist covering widget best practices, state management patterns (BLoC, Riverpod, Provider, GetX, MobX, Signals), Dart idioms, performance, accessibility, security, and clean architecture.

— No reviews yet
0 installs
33 views
0.0% view→install

Install

$ agentstack add skill-affaan-m-ecc-flutter-dart-code-review

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ● Environment & secrets Used
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-affaan-m-ecc-flutter-dart-code-review)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Flutter Dart Code Review? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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 maintainers
  • [ ] Check license compatibility with your project
  • [ ] Verify platform support covers your targets

Version constraints:

  • [ ] Use caret syntax (^1.2.3) for dependencies — allows compatible updates
  • [ ] Pin exact versions only when absolutely necessary
  • [ ] Run flutter pub outdated regularly to track stale dependencies
  • [ ] No dependency overrides in production pubspec.yaml — only for temporary fixes with a comment/issue link
  • [ ] Minimize transitive dependency count — each dependency is an attack surface

Monorepo-specific (melos/workspace):

  • [ ] Internal packages import only from public API — no `package:other/src/internal.

…

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.