| name | flutter-clean-architecture |
| description | Scaffold and review Flutter apps built with Clean Architecture + MVVM on Riverpod, Freezed, GoRouter, and result_dart, using a Command pattern for async UI, a custom design-system theme, and ARB localization. Use when creating a Flutter feature, wiring repositories / view-models / providers, enforcing layer boundaries, or reviewing Flutter code against this architecture. |
Flutter Clean Architecture + MVVM (Riverpod)
A reusable architecture for any Flutter app: tasks, social, healthcare, e‑commerce,
dashboards — any domain. It defines folder conventions, layering rules, state
management, error handling, theming, and localization so generated code stays
consistent and testable.
Package name: examples import package:app/.... Replace app with the
name: from your project's pubspec.yaml.
When to use
Apply this skill when the user wants to:
- Start a new Flutter app with a clear, layered architecture.
- Add a feature (entity → repository → view-model → screen → route).
- Wire dependency injection with Riverpod.
- Review Flutter code for layer-boundary or state-management violations.
The layer model
UI ──────────────────────────► Domain (entities · interfaces · errors · services)
▲
Data ───────────────────────────┘ (implements domain interfaces)
- Domain is pure Dart — zero Flutter imports.
- Data implements domain interfaces, never imports UI.
- UI depends on domain models + interfaces (via Riverpod providers), never on
concrete
*_impl.dart files.
- Every fallible operation in domain and data returns
Result<T> (result_dart).
| Layer | May import | Must NOT import |
|---|
| Domain | pure Dart, freezed_annotation, json_annotation, result_dart | Flutter, data/, ui/, any HTTP/storage pkg |
| Data | domain/ (models, interfaces, errors), Dio, SharedPreferences, SecureStorage | ui/, view-models |
| UI | domain/, providers/, routing/, l10n/, auth/, Flutter | data/repositories/*_impl.dart, data/services/ |
| Providers | data/ implementations, domain/ interfaces, riverpod_annotation | ui/views/ |
Non-negotiable rules
Layer boundaries
- No business logic in widgets. Widgets render state and emit events; decisions live in the view-model.
- No API calls from the UI. Flow is always
RemoteService → Repository → ViewModel → View.
- Never import
data/repositories/*_impl.dart or data/services/ into widgets — use the abstract interface via a Riverpod provider.
- Never import Flutter into
domain/. Domain is pure Dart.
- Domain must not know JSON keys, HTTP, or storage.
fromJson on entities is allowed; Dio/SharedPreferences imports are not.
State management
6. Async actions use Command / Command1<T> — never isLoading/isError flags on the state object.
7. State objects are @freezed sealed class.
8. View-models are @riverpod class XxxViewModel extends _$XxxViewModel. No ChangeNotifier, legacy StateNotifier, or BLoC.
9. Singleton providers (services, repos, router) use @Riverpod(keepAlive: true). Feature view-models use plain @riverpod (auto-dispose).
Errors
10. Never show AppException.message to users — use AppExceptionL10n.localizedMessage(l).
11. Never show ApiException or technicalDetails to users; log them only.
12. Use Result<T> for every fallible domain/data operation.
Localization
13. No hardcoded user-facing strings in widgets — all strings live in ARB files.
14. Add every locale's translation together when introducing new text.
15. Read strings via AppLocalizations.of(context)!.yourKey.
Theme
16. No hardcoded colors, font sizes, or radii in widgets — read from AppTheme.of(context).
17. Never use Flutter's Theme.of(context) for design-system values; AppTheme.of(context) is the source of truth.
18. New tokens go into AppColorsData / AppTypographyData / AppBordersData.
Routing
19. No string literals for paths — use AppRoutes.xxx constants.
20. View-models do not navigate. They update auth/state; the router reacts. For programmatic nav from a view, use context.go(AppRoutes.xxx).
Code generation
21. Run dart run build_runner build --delete-conflicting-outputs after editing any @freezed, @riverpod, or @JsonSerializable class.
22. Never hand-edit *.freezed.dart or *.g.dart.
Testing
23. Mock view-models by extending them with Mock (mocktail) and override the provider via ProviderScope(overrides: [...]).
24. Use pumpApp() from test/helpers/test_helpers.dart for widget tests needing theme + l10n.
Adding a feature (overview)
For an entity Article, create in order:
domain/models/article/article.dart — @freezed entity with fromJson.
data/repositories/article/article_repository.dart — abstract interface, Future<Result<T>>.
data/services/remote/article_remote_service.dart — returns raw Result<Map<String,dynamic>>.
data/repositories/article/article_repository_impl.dart — calls Article.fromJson, wraps errors as AppException.
data/repositories/article/local_article_repository.dart — stub for local dev.
providers/article_repository_provider.dart — @Riverpod(keepAlive: true).
ui/views/articles/view_models/articles_view_model.dart — state + @riverpod notifier + Command.
ui/views/articles/widgets/articles_screen.dart — ConsumerWidget.
- Add route to
AppRoutes + GoRoute.
- Add strings to every
app_*.arb, run flutter gen-l10n.
- Override the repo provider in
main_local.dart.
- Add a widget test under
test/widget/organisms/.
Run dart scripts/scaffold_feature.dart Article to generate steps 1–8 as stub files.
Full copy-paste code for every step: reference/add-feature.md.
Project structure
lib/
├── main.dart / main_local.dart / main_staging.dart # entry points
├── app.dart # root ConsumerWidget
├── config/app_config.dart # env via --dart-define
├── auth/auth_provider.dart # global auth state (Riverpod Notifier)
├── routing/app_router.dart # GoRouter + auth redirect
├── providers/ # Riverpod DI — one file per dependency
├── domain/
│ ├── errors/ # AppException, AppErrorCode, ServerErrorCode
│ ├── enums/
│ ├── models/ # @freezed entities, one folder each
│ └── services/ # pure-Dart domain services
├── data/
│ ├── errors/api_exception.dart # transport-level only
│ ├── repositories/<feature>/ # interface + impl + local
│ └── services/{local,remote}/ # SharedPreferences/SecureStorage + Dio services
├── ui/
│ ├── core/{base,command,themes,ui}/ # ui = atoms/molecules/organisms
│ ├── extensions/app_exception_l10n.dart
│ └── views/<feature>/{view_models,widgets}/
├── utils/
└── l10n/ # app_en.arb, app_<locale>.arb, generated files
Reference files
reference/architecture.md — full detail: imports table, error hierarchy, Command pattern, DI graph, routing bridge, theme tokens, l10n, bootstrap flow, data-flow diagrams.
reference/add-feature.md — the complete 12-step feature walkthrough with copy-paste code.
reference/review-checklist.md — pre-submit checklist for reviewing generated code.
scripts/scaffold_feature.dart — generates a feature's stub files from a name.