Project Structure
- Keep code scalable as features grow.
- Make features isolated (easy to add/remove/refactor).
- Put shared utilities in core, not duplicated across features.
- Use Riverpod for dependency injection + state management in a consistent way.
Folder Structure (Standard)
lib/ core/ themes/ config/ const/ errors/ model/ router/ network/ providers/ services/ widgets/
features/ <feature_name>/ screen/ widget/ provider/ service/ model/
main.dartNaming conventions
- folders/files:
snake_case - feature folder:
auth,profile,cart - screens:
*_screen.dart - widgets:
*_widget.dart - providers:
*_provider.dart - services:
*_service.dart - models:
*_model.dart
Core vs Feature Responsibilities
core/ (shared, reusable across features)
Use core/ for anything that is:
- used by 2+ features
- infrastructure / app-wide concerns
Core contents
core/widgets/: shared UI building blocks (buttons, loaders, empty states)core/providers/: app-wide providers (dio client, app config, auth session, logger)core/themes: Overall all themes like dark,light and other based on app pref.core/network/: HTTP client setup, interceptors, API base abstractionscore/const/: app constants (spacing, strings, keys)core/config/: environment/config setup (flavors, endpoints mapping if global)core/errors/: failure models, error mappers, exceptionscore/model/: shared models used across features (rare; keep minimal)core/services/: shared services (storage, analytics, notifications)
features/<feature>/ (owns its domain)
Each feature owns:
- UI (
screen/,widget/) - state (
provider/) - domain/service calls (
service/) - models specific to that feature (
model/)
Rule: A feature should not import another feature directly.
If something is shared, move it to core/.