Skip to content

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.dart

Naming 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 abstractions
  • core/const/: app constants (spacing, strings, keys)
  • core/config/: environment/config setup (flavors, endpoints mapping if global)
  • core/errors/: failure models, error mappers, exceptions
  • core/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/.