Template: Clean Architecture Feature
The canonical end-to-end Flutter feature: all three layers, the Riverpod DI graph, typed error handling, and tests. This bundle ships representative .dart files you can copy and rename. See architecture/clean_architecture.md and skills/architecture/flutter/clean_architecture.md.
Folder Structure
text
clean_architecture_feature/
├── domain.dart // entity + Failure union + repository interface + use cases
├── data.dart // DTO + mapper + remote data source + repository impl
├── presentation.dart // AsyncNotifier + ConsumerWidget with every state handled
├── providers.dart // composition root — the DI graph
└── feature_test.dart // mapper + use case + notifier + widget testsThe files use a sample
Articlefeature. ReplaceArticle/articlewith your entity, and split each file into one-type-per-file when you copy it into a real module. They are illustrative — they are not part of a compiled package, so cross-file symbols won't resolve in isolation.
How the layers connect
text
ArticlesScreen → ArticlesNotifier → GetArticles → ArticleRepository (interface)
▲
ArticleRepositoryImpl (Data) ── ArticleRemoteDataSource ── DioConventions Demonstrated
- Domain is framework-free —
domain.dartimports nothing. The entity, theFailureunion, the repository interface, and the use cases all live there. - Data owns the wire format —
ArticleDtoand itstoEntity()mapper never leavedata.dart, and everyDioExceptionis translated into aFailurebefore it crosses the boundary. - Presentation is a function of state — the widget renders loading / loaded / empty / error and nothing else;
_messageFormaps typed failures to user-facing copy. - Providers are typed as Domain interfaces (
Provider<ArticleRepository>), which is what makes every edge overridable in a test. - Tests override the repository seam with a hand-written fake, so the real notifier and use case logic actually runs.
addTearDown(container.dispose)prevents state leaking between tests. - Keys on everything a test drives (
articles.loading,articles.empty,articles.error,articles.retry) so finders are stable.
Usage
- Copy the bundle into your feature module (
lib/features/<name>/). - Rename
Article/article→ your entity, and split into per-type files. - Point
dioProvider'sbaseUrlat your configuration provider rather than a literal. - Register the screen in the
go_routerroute table (skills/architecture/flutter/router_navigation.md). - Run
flutter analyzeandflutter test.