Reviews Flutter/Dart code with library-agnostic checklist for widget best practices, state management patterns, Dart idioms, performance, accessibility, security, and clean architecture.
From everything-claude-codenpx claudepluginhub burgebj/claude_everythingThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
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.
pubspec.yaml is clean — no unused dependencies, versions pinned appropriatelyanalysis_options.yaml includes a strict lint set with strict analyzer settings enabledprint() statements in production code — use dart:developer log() or a logging package.g.dart, .freezed.dart, .gr.dart) are up-to-date or in .gitignoredynamic — enable strict-casts, strict-inference, strict-raw-types! (bang operator) instead of proper null checks or Dart 3 pattern matching (if (value case var v?))this.field where local variable promotion would workcatch (e) without on clause; always specify exception typesError: Error subtypes indicate bugs and should not be caughtasync: Functions marked async that never await — unnecessary overheadlate overuse: late used where nullable or constructor initialization would be safer; defers errors to runtimeStringBuffer instead of + for iterative string buildingconst contexts: Fields in const constructor classes should not be mutableFuture return values: Use await or explicitly call unawaited() to signal intentvar where final works: Prefer final for locals and const for compile-time constantspackage: imports for consistencyList/Mapif-case over verbose is checks and manual casting(String, int) instead of single-use DTOsprint() in production code: Use dart:developer log() or the project's logging package; print() has no log levels and cannot be filteredbuild() method exceeding ~80-100 lines_build*() helper methods that return widgets are extracted to separate widget classes (enables element reuse, const propagation, and framework optimizations)const constructors used wherever possible — prevents unnecessary rebuildsconst literals for collections that don't change (const [], const {})const when all fields are finalValueKey used in lists/grids to preserve state across reordersGlobalKey used sparingly — only when accessing state across the tree is truly neededUniqueKey avoided in build() — it forces rebuild every frameObjectKey used when identity is based on a data object rather than a single valueTheme.of(context).colorScheme — no hardcoded Colors.red or hex valuesTheme.of(context).textTheme — no inline TextStyle with raw font sizesbuild()Future.then() or async work in build().listen()) in build()setState() localized to smallest possible subtreeThese principles apply to all Flutter state management solutions (BLoC, Riverpod, Provider, GetX, MobX, Signals, ValueNotifier, etc.).
ref.watch is expected — flag only circular or overly tangled chainscopyWith() or constructors, never mutated in-place== and hashCode properly (all fields included in comparison)Equatable, freezed, Dart records, or otherList/Map@action in MobX, .value on signals, .obs in GetX) — direct field mutation bypasses change trackingReactionDisposer in MobX, effect cleanup in Signals)AsyncValue) — not boolean flags (isLoading, isError, hasData)// 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
const widgets used to stop rebuild propagation through the tree.listen()) are cancelled in dispose() / close().listen())mounted check before setState in async callbacksBuildContext not used after await without checking context.mounted (Flutter 3.7+) — stale context causes crashesBuildContext never stored in singletons, state managers, or static fieldssetState, ValueNotifier)setState() not called at root widget level — localize state changesconst widgets used to stop rebuild propagationRepaintBoundary used around complex subtrees that repaint independentlyAnimatedBuilder child parameter used for subtrees independent of animationbuild() — compute in state management layerbuild()MediaQuery.of(context) usage is specific (e.g., MediaQuery.sizeOf(context))Image.asset with cacheWidth/cacheHeight to decode at display sizeListView.builder / GridView.builder used instead of ListView(children: [...]) for large or dynamic lists (concrete constructors are fine for small, static lists)deferred as) used for heavy libraries in web buildsOpacity widget avoided in animations — use AnimatedOpacity or FadeTransitionoperator == not overridden on widgets — use const constructors insteadIntrinsicHeight, IntrinsicWidth) used sparingly (extra layout pass)pumpWidget and pump used correctly for async operationsfind.byType, find.text, find.byKey used appropriatelypumpAndSettle or explicit pump(Duration)Semantics widget used to provide screen reader labels where automatic labels are insufficientExcludeSemantics used for purely decorative elementsMergeSemantics used to combine related widgets into a single accessible elementsemanticLabel property setonPressed callbacks — every button does something or is disabledSafeArea widgetAndroidManifest.xml and Info.plistLayoutBuilder or MediaQuery used for responsive layoutsFlexible, Expanded, FittedBox--dart-define, .env files excluded from VCS, or compile-time configuration.gitignore^1.2.3) for dependencies — allows compatible updatesflutter pub outdated regularly to track stale dependenciespubspec.yaml — only for temporary fixes with a comment/issue linkpackage:other/src/internal.dart (breaks Dart package encapsulation)path: ../../ relative stringsanalysis_options.yamlNavigator.push with a declarative routerMap<String, dynamic> or Object? castingFlutterError.onError overridden to capture framework errors (build, layout, paint)PlatformDispatcher.instance.onError set for async errors not caught by FlutterErrorWidget.builder customized for release mode (user-friendly instead of red screen)runApp (e.g., runZonedGuarded, Sentry/Crashlytics wrapper)if checksanalysis_options.yaml present with strict settings enabledstrict-casts: true, strict-inference: true, strict-raw-types: true// ignore:) are justified with comments explaining whyflutter analyze runs in CI and failures block mergesprefer_const_constructors — performance in widget treesavoid_print — use proper loggingunawaited_futures — prevent fire-and-forget async bugsprefer_final_locals — immutability at variable levelalways_declare_return_types — explicit contractsavoid_catches_without_on_clauses — specific error handlingalways_use_package_imports — consistent import styleThe table below maps universal principles to their implementation in popular solutions. Use this to adapt review rules to whichever solution the project uses.
| Principle | BLoC/Cubit | Riverpod | Provider | GetX | MobX | Signals | Built-in |
|---|---|---|---|---|---|---|---|
| State container | Bloc/Cubit | Notifier/AsyncNotifier | ChangeNotifier | GetxController | Store | signal() | StatefulWidget |
| UI consumer | BlocBuilder | ConsumerWidget | Consumer | Obx/GetBuilder | Observer | Watch | setState |
| Selector | BlocSelector/buildWhen | ref.watch(p.select(...)) | Selector | N/A | computed | computed() | N/A |
| Side effects | BlocListener | ref.listen | Consumer callback | ever()/once() | reaction | effect() | callbacks |
| Disposal | auto via BlocProvider | .autoDispose | auto via Provider | onClose() | ReactionDisposer | manual | dispose() |
| Testing | blocTest() | ProviderContainer | ChangeNotifier directly | Get.put in test | store directly | signal directly | widget test |