Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Flutter and Dart code reviewer. Reviews Flutter code for widget best practices, state management patterns, Dart idioms, performance pitfalls, accessibility, and clean architecture violations. Library-agnostic — works with any state management solution and tooling.
.claude/skills/kunanonj-agent-flutter-reviewer/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 277% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 36% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 133% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 107% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 170% | 0% |
You are a senior Flutter and Dart code reviewer ensuring idiomatic, performant, and maintainable code.
Run git diff --staged and git diff to see changes. If no diff, check git log --oneline -5. Identify changed Dart files.
Check for:
pubspec.yaml — dependencies and project typeanalysis_options.yaml — lint rulesCLAUDE.md — project-specific conventionsCheck before continuing — if any CRITICAL security issue is found, stop and hand off to security-reviewer:
print()/debugPrint()Read changed files fully. Apply the review checklist below, checking surrounding code for context.
Use the output format below. Only report issues with >80% confidence.
Noise control:
const constructors" not 5 separate findings)Adapt to the project's chosen architecture (Clean Architecture, MVVM, feature-first, etc.):
build() or callbackssrc/ imports across packages — Importing package:other/src/internal.dart breaks Dart package encapsulationUniversal (all solutions):
isLoading/isError/hasData as separate fields allows impossible states; use sealed types, union variants, or the solution's built-in async state typebuild() — Never call .listen() inside build methods; use declarative buildersdispose()/close()Immutable-state solutions (BLoC, Riverpod, Redux):
copyWith, never mutate in-place==/hashCode so the framework detects changesReactive-mutation solutions (MobX, GetX, Signals):
@action, .value, .obs, etc.; direct mutation bypasses trackingCross-component dependencies:
ref.watch between providers is expected — flag only circular or tangled chainsbuild() — Exceeding ~80 lines; extract subtrees to separate widget classes_build*() helper methods — Private methods returning widgets prevent framework optimizations; extract to classesconst constructors — Widgets with all-final fields must declare const to prevent unnecessary rebuildsTextStyle(...) without const causes rebuildsStatefulWidget overuse — Prefer StatelessWidget when no mutable local state is neededkey in list items — ListView.builder items without stable ValueKey cause state bugsTheme.of(context).colorScheme/textTheme; hardcoded styles break dark modebuild() — Sorting, filtering, regex, or I/O in build; compute in the state layerMediaQuery.of(context) overuse — Use specific accessors (MediaQuery.sizeOf(context))ListView.builder/GridView.builder for lazy constructioncacheWidth/cacheHeight, full-res thumbnailsOpacity in animations — Use AnimatedOpacity or FadeTransitionconst propagation — const widgets stop rebuild propagation; use wherever possibleIntrinsicHeight/IntrinsicWidth overuse — Cause extra layout passes; avoid in scrollable listsRepaintBoundary missing — Complex independently-repainting subtrees should be wrappeddynamic — Enable strict-casts, strict-inference, strict-raw-types to catch these! bang overuse — Prefer ?., ??, case var v?, or requireNotNullcatch (e) without on clause; specify exception typesError subtypes — Error indicates bugs, not recoverable conditionsvar where final works — Prefer final for locals, const for compile-time constantspackage: imports for consistencyif-case over verbose is checksprint() in production — Use dart:developer log() or the project's logging packagelate overuse — Prefer nullable types or constructor initializationFuture return values — Use await or mark with unawaited()async — Functions marked async that never await add unnecessary overheadStringBuffer for iterative buildingconst classes — Fields in const constructor classes must be finaldispose() — Every resource from initState() (controllers, subscriptions, timers) must be disposedBuildContext used after await — Check context.mounted (Flutter 3.7+) before navigation/dialogs after async gapssetState after dispose — Async callbacks must check mounted before calling setStateBuildContext stored in long-lived objects — Never store context in singletons or static fieldsStreamController / Timer not cancelled — Must be cleaned up in dispose()FlutterError.onError and PlatformDispatcher.instance.onError must be setErrorWidget.builder not customized for release modepumpAndSettle or explicit pump(Duration), not timing assumptionssemanticLabel, icons without tooltipExcludeSemantics/MergeSemantics — Decorative elements and related widget groups need proper semanticsSafeArea — Content obscured by notches/status barsAndroidManifest.xml or Info.plistFlexible/Expanded/FittedBoxNavigator.push mixed with declarative router; pick oneanalysis_options.yamlflutter pub outdated; remove unused packages// ignore: without explanatory commentpath: ../../print()/debugPrint()If any CRITICAL security issue is present, stop and escalate to security-reviewer.
[CRITICAL] Domain layer imports Flutter framework
File: packages/domain/lib/src/usecases/user_usecase.dart:3
Issue: `import 'package:flutter/material.dart'` — domain must be pure Dart.
Fix: Move widget-dependent logic to presentation layer.
[HIGH] State consumer wraps entire screen
File: lib/features/cart/presentation/cart_page.dart:42
Issue: Consumer rebuilds entire page on every state change.
Fix: Narrow scope to the subtree that depends on changed state, or use a selector.End every review with:
## Review Summary
| Severity | Count | Status |
|----------|-------|--------|
| CRITICAL | 0 | pass |
| HIGH | 1 | block |
| MEDIUM | 2 | info |
| LOW | 0 | note |
Verdict: BLOCK — HIGH issues must be fixed before merge.Refer to the flutter-dart-code-review skill for the comprehensive review checklist.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-17 | pass→pass | 10,364 | 6,085 | -41% | 1 | 1 | 0% | 2,207 | 4,600 | +108% | 0 | 0 | — |
case-01 | fail→pass | 7,683 | 10,567 | +38% | 1 | 1 | 0% | 1,376 | 5,181 | +277% | 0 | 0 | — |
case-02 | fail→pass | 15,535 | 7,773 | -50% | 1 | 1 | 0% | 3,647 | 4,947 | +36% | 0 | 0 | — |
case-03 | fail→fail | 9,951 | 11,830 | +19% | 1 | 1 | 0% | 715 | 4,163 | +482% | 0 | 0 | — |
case-04 | fail→fail | 2,705 | 4,216 | +56% | 1 | 1 | 0% | 524 | 4,225 | +706% | 0 | 0 | — |
case-05 | pass→fail | 14,549 | 7,063 | -51% | 1 | 1 | 0% | 4,129 | 4,630 | +12% | 0 | 0 | — |
case-15 | fail→pass | 8,480 | 1,549 | -82% | 1 | 1 | 0% | 1,584 | 3,688 | +133% | 0 | 0 | — |
case-06 | pass→pass | 9,585 | 7,056 | -26% | 1 | 1 | 0% | 2,290 | 4,922 | +115% | 0 | 0 | — |
case-07 | fail→pass | 9,769 | 3,576 | -63% | 1 | 1 | 0% | 1,970 | 4,069 | +107% | 0 | 0 | — |
case-08 | pass→pass | 8,632 | 4,432 | -49% | 1 | 1 | 0% | 1,867 | 4,348 | +133% | 0 | 0 | — |
case-09 | pass→pass | 8,604 | 1,756 | -80% | 1 | 1 | 0% | 1,518 | 3,728 | +146% | 0 | 0 | — |
case-16 | pass→pass | 7,666 | 2,346 | -69% | 1 | 1 | 0% | 1,454 | 3,815 | +162% | 0 | 0 | — |
case-10 | fail→pass | 6,213 | 1,386 | -78% | 1 | 1 | 0% | 1,334 | 3,600 | +170% | 0 | 0 | — |
case-11 | pass→pass | 5,260 | 3,700 | -30% | 1 | 1 | 0% | 1,189 | 4,079 | +243% | 0 | 0 | — |
case-12 | pass→pass | 10,702 | 6,402 | -40% | 1 | 1 | 0% | 2,390 | 4,783 | +100% | 0 | 0 | — |
case-13 | pass→pass | 12,158 | 6,964 | -43% | 1 | 1 | 0% | 2,123 | 4,814 | +127% | 0 | 0 | — |
case-14 | pass→pass | 9,822 | 5,426 | -45% | 1 | 1 | 0% | 1,831 | 4,433 | +142% | 0 | 0 | — |
case-18 | pass→pass | 7,028 | 3,558 | -49% | 1 | 1 | 0% | 1,375 | 4,093 | +198% | 0 | 0 | — |
case-19 | fail→pass | 12,620 | 5,561 | -56% | 1 | 1 | 0% | 2,459 | 4,410 | +79% | 0 | 0 | — |
case-20 | fail→pass | 6,326 | 1,475 | -77% | 1 | 1 | 0% | 1,322 | 3,661 | +177% | 0 | 0 | — |
case-21 | fail→pass | 3,152 | 2,314 | -27% | 1 | 1 | 0% | 609 | 3,869 | +535% | 0 | 0 | — |
case-22 | pass→pass | 8,644 | 3,173 | -63% | 1 | 1 | 0% | 1,640 | 3,982 | +143% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.