What I do
- Enforce feature-based project architecture with clean layer separation
- Guide state management with Riverpod and immutable models with freezed
- Apply error handling patterns and widget testing conventions
When to use me
Use this when working on Flutter/Dart projects with Riverpod state management.
Key Rules
- Use feature-based directory structure:
lib/core/,lib/features/<name>/{data,domain,presentation}/,lib/shared/ - Prefer Riverpod (
StateNotifierProvider,ConsumerWidget) for app/shared state; usesetStatefor simple local UI state - Prefer immutable data models (for example
freezed) for domain/network DTOs; avoid mutable models unless justified - Error handling via sealed
Result<T>class:Success<T>andError<T>— never throw across layers - Prefer
StatelessWidgetandConsumerWidget; only useStatefulWidgetwhen absolutely necessary - Mark widgets and constructors
constwherever possible — the analyzer will catch misses - Prefer
go_routerfor app-scale declarative routing;NavigatorAPIs are fine for simple/local flows - Use
ThemeExtension<T>for custom theme properties — never hardcode colors or spacing - Riverpod providers serve as dependency injection — no service locators or get_it needed
- Wrap async calls in try/catch at the repository layer; return
Result, not raw exceptions - Require widget tests for critical UI behavior; use risk-based coverage across unit/widget/integration tests
- Use
super.keyin widget constructors (Dart 3+), notKey? keywithsuper(key: key) - Use a verify-fix-verify loop: run the validation commands below, fix any failures, and rerun until all checks pass
Naming Conventions
PascalCasefor classes/widgetscamelCasefor methods/variablessnake_case.dartfor file names- Name providers by domain intent (
userProfileProvider,checkoutNotifierProvider)
Common Pitfalls
- Business logic embedded directly in widgets
- Mutable model classes instead of immutable data contracts
- Async exceptions crossing layers without normalization to
Result - Hardcoded spacing/colors that bypass theme and break responsive consistency
Example Patterns
final userProvider = FutureProvider<Result<User>>((ref) async {
try {
final user = await ref.read(apiClientProvider).getUser();
return Result.success(user);
} catch (e) {
return Result.error('Failed to load user: $e');
}
});
// Result<T> is an app-defined sealed type used for cross-layer error handling.
Validation Commands
Prefer project scripts when present; use defaults below otherwise.
flutter analyze
flutter test
flutter test --coverage
dart format --set-exit-if-changed .
Source: shahboura/agents-opencode — distributed by TomeVault.