Production-oriented Flutter starter kit with clean feature boundaries, sample auth and product flows, offline-first product caching, and test coverage for core business logic.
Most starter projects are either too thin to be useful or so opinionated that they are hard to adapt. This project aims for the middle:
- enough structure to start real feature work quickly
- enough examples to demonstrate app flow, state, persistence, and testing
- enough restraint to stay reusable instead of becoming framework ceremony
- Feature-first structure with
data,domain, andpresentationlayers - Login flow backed by DummyJSON
POST /auth/login - Persisted auth session using
flutter_secure_storage - Protected navigation using
go_routerand an auth guard - Product listing backed by DummyJSON
GET /productsandGET /products/search - Search, sorting, order toggle, pull-to-refresh, and pagination
- Local product cache using Drift for the default listing
- Dependency injection with
get_it - Immutable models and states with
freezedandjson_serializable - Unit tests for repositories, mappers, and BLoCs
- backend ownership or production API contracts
- analytics, crash reporting, or remote config wiring
- design system token packages or multi-brand theming
- CI/CD workflows
- integration and end-to-end tests
Those are valid additions, but they are left out so the starter stays understandable and easy to extend.
Place the recorded demo GIF at docs/demo/app_demo.gif.
- Login
- Product list
- Product list with search and sorting
Each feature follows the same layered split:
data: remote/local datasources, DTOs, repository implementations, mappersdomain: entities/models, repository contracts, use casespresentation: screens, widgets, BLoCs, events, states
Shared app concerns live under lib/core:
config: flavor and API base URL resolutiondata/remote: Dio client and safe API wrapperdata/local: Drift databasedi: service registrationpresentation/router: route definitions and auth redirect logicdomain/models: shared result and error types
Screen -> BLoC -> UseCase -> Repository -> DataSource
|-> Remote API via Dio
|-> Local storage via Drift / Secure Storage
For the default product listing, the flow is slightly richer:
ProductScreen -> ProductBloc
-> load cached list first
-> refresh from API
-> update Drift cache
-> show stale cached data if refresh fails
lib/
app.dart
main.dart
core/
config/
data/
local/
remote/
di/
domain/models/
presentation/
constants/
router/
theme/
widgets/
features/
auth/
data/
datasources/
local/
mappers/
remote/model/dtos/
repositories/
domain/
models/
repositories/
usecases/
presentation/
bloc/
constants/
screens/
product/
data/
datasources/
local/
database/
datasources/
mappers/
model/
mappers/
remote/model/dtos/
repositories/
domain/
models/
repositories/
usecases/
presentation/
bloc/
constants/
screens/
widgets/
test/
features/
auth/
product/
- The app starts at
/login - Demo credentials are predeclared in the UI:
emilys / emilyspass - Successful login stores the session in secure storage
AuthGuardrestores the saved session on startup- Logged-in users are redirected to
/product
- Default listing loads cached data first when available
- A background refresh updates the cache from the API
- If refresh fails but cache exists, the UI keeps showing stale data with an error banner
- Search requests use
/products/search - Sorting supports
title,price, andrating - Load-more paging is handled in
ProductBloc
Use the existing auth and product modules as references.
- Create
data,domain, andpresentationfolders underlib/features/<feature_name>/ - Define the repository contract and use cases in
domain/ - Implement datasources, DTOs, mappers, and repository logic in
data/ - Build the screen and BLoC in
presentation/ - Register dependencies in
lib/core/di/injection.dart - Add routing in
lib/core/presentation/router/app_router.dartif the feature has a screen - Add focused tests under
test/features/<feature_name>/
The app reads compile-time values from dart-define.
flutter run --dart-define=APP_FLAVOR=dev
flutter run --dart-define=APP_FLAVOR=staging
flutter run --dart-define=APP_FLAVOR=prodCurrent defaults:
dev->https://dummyjson.comstaging->https://dummyjson.comprod->https://dummyjson.com
flutter run --dart-define=API_BASE_URL=https://api.example.com- Flutter
flutter_blocgo_routerdioget_itdriftanddrift_flutterflutter_secure_storagefreezedjson_serializableshimmer
- Flutter SDK
- Dart SDK
^3.11.0
flutter pub getIf CocoaPods reports an outdated specs repository, run:
cd ios
pod install --repo-updateflutter runWith an explicit flavor:
flutter run --dart-define=APP_FLAVOR=devRun this after changing freezed, json_serializable, or Drift-backed models:
dart run build_runner build --delete-conflicting-outputsflutter analyze
flutter test- Auth repository tests
- Auth BLoC tests
- Product mapper tests
- Product repository tests
- Product BLoC tests
- Add environment-specific endpoints instead of using DummyJSON for every flavor
- Expand feature modules from the provided auth and product examples
- Add integration tests for navigation and persisted session restoration
- logout use case and session-expiry handling
- reusable pagination primitives
- richer offline strategy for filtered product queries
- feature scaffolding script or template
- CI checks for analyze, test, and code generation



