| name | flutter-dart-code-review |
| description | 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. |
| origin | ECC |
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
2. Dart Language Pitfalls
3. Widget Best Practices
Widget decomposition:
Const usage:
Key usage:
Theming & design system:
Build method complexity:
4. State Management (Library-Agnostic)
These principles apply to all Flutter state management solutions (BLoC, Riverpod, Provider, GetX, MobX, Signals, ValueNotifier, etc.).
Architecture:
Immutability & value equality (for immutable-state solutions: BLoC, Riverpod, Redux):
Reactivity discipline (for reactive-mutation solutions: MobX, GetX, Signals):
State shape design:
// 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:
Subscriptions & disposal:
Local vs global state:
5. Performance
Unnecessary rebuilds:
Expensive operations in build():
Image optimization:
Lazy loading:
Other:
6. Testing
Test types and expectations:
Coverage targets:
Test isolation:
Widget test quality:
7. Accessibility
Semantic widgets:
Screen reader support:
Visual accessibility:
Interaction accessibility:
8. Platform-Specific Concerns
iOS/Android differences:
Responsive design:
9. Security
Secure storage:
API key handling:
Input validation:
Network security:
10. Package/Dependency Review
Evaluating pub.dev packages:
Version constraints:
Monorepo-specific (melos/workspace):
11. Navigation and Routing
General principles (apply to any routing solution):
12. Error Handling
Framework error handling:
Error reporting:
Graceful degradation:
13. Internationalization (l10n)
Setup:
Content:
Code review:
14. Dependency Injection
Principles (apply to any DI approach):
15. Static Analysis
Configuration:
Enforcement:
Key rules to verify regardless of lint package:
State Management Quick Reference
The 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 |
Sources