← Articles

Feature-first folder structure in Flutter

By Ann Tech · 24 November 2024

Folder structure is architecture made visible. Feature-first organization groups everything related to one feature together — making it easy to find code, delete features, and reason about boundaries.

Layer-first vs feature-first

Layer-first (common anti-pattern for large apps):

lib/
  models/        # All models from all features
  repositories/  # All repositories
  blocs/         # All BLoCs
  screens/       # All screens
  widgets/       # All widgets

Problems: changing a feature means touching 5+ folders. Deleting a feature is error-prone. New developers can't tell what the app does.

Feature-first (recommended):

lib/
  features/
    auth/
      data/
        auth_repository.dart
        auth_api.dart
        models/
          user.dart
          auth_response.dart
      domain/
        auth_service.dart
      presentation/
        screens/
          login_screen.dart
          register_screen.dart
        widgets/
          auth_form.dart
        bloc/
          auth_bloc.dart
          auth_event.dart
          auth_state.dart
    products/
      data/
      presentation/
    orders/
      data/
      presentation/
  core/
    network/
    theme/
    utils/
  app/
    app.dart
    router.dart

Practical structure (simpler variant)

For apps that don't need strict DDD layers:

lib/
  features/
    auth/
      auth_repository.dart
      auth_state.dart       # Riverpod notifier or BLoC
      login_screen.dart
      register_screen.dart
      auth_widgets.dart     # Shared within feature
    products/
      product.dart          # Model
      products_repository.dart
      products_notifier.dart
      products_screen.dart
      product_detail_screen.dart
      product_card.dart
    cart/
      cart.dart
      cart_notifier.dart
      cart_screen.dart
    orders/
      order.dart
      orders_repository.dart
      orders_screen.dart
      order_detail_screen.dart
  shared/
    widgets/
      app_button.dart
      app_text_field.dart
      loading_overlay.dart
    models/
      pagination.dart
      api_response.dart
    utils/
      date_utils.dart
      currency_utils.dart
  core/
    di/
      providers.dart        # All Riverpod providers (or BLoC DI)
    network/
      dio_client.dart
      auth_interceptor.dart
    storage/
      secure_storage.dart
      app_preferences.dart
    theme/
      app_theme.dart
      app_colors.dart
  app/
    app.dart
    router.dart
  main.dart
  main_dev.dart
  main_staging.dart

What goes in core/ vs shared/

core/ — infrastructure and cross-cutting concerns:

  • Network client (Dio setup)
  • Authentication token handling
  • Dependency injection
  • Logging, crash reporting
  • Theme definitions

shared/ — reusable UI and domain primitives:

  • Design system widgets (buttons, inputs, cards)
  • Common models used across features (pagination, API error)
  • Utility functions (date formatting, currency)

Feature boundaries

A feature owns its internal implementation. Other features must not import from inside a feature's internals:

// WRONG: products feature importing orders' internal model
import 'package:myapp/features/orders/order_item.dart';

// RIGHT: shared model used by both features
import 'package:myapp/shared/models/order_summary.dart';

If two features need to share a model, move it to shared/.

Barrel files

For clean imports, create a barrel file per feature:

// features/products/products.dart
export 'product.dart';
export 'products_repository.dart';
export 'products_notifier.dart';
export 'products_screen.dart';
export 'product_detail_screen.dart';
// In other files:
import 'package:myapp/features/products/products.dart';
// vs
import 'package:myapp/features/products/product.dart';
import 'package:myapp/features/products/products_repository.dart';
// ...

Scaling the structure

For very large apps, features can be extracted into separate Dart packages using Melos:

packages/
  feature_auth/
    lib/
    test/
    pubspec.yaml
  feature_products/
  feature_orders/
  shared_ui/     # Design system components
  core_network/
apps/
  my_app/        # Assembles features

This enforces boundaries at the package level and enables parallel builds.

Common pitfalls

A utils/ folder that becomes a dumping ground. Anything that "doesn't fit" ends up in utils/. After 6 months, utils/ has 30 files and nobody knows what's in it. Be specific: date_utils.dart, currency_format.dart — not a catch-all helpers.dart.

Screens importing from other screens directly. If ProductDetailScreen imports from OrdersScreen, you've created a hidden coupling. Screens should only import from their own feature folder and shared/.

Reorganizing structure without team alignment. Structure changes create large git diffs that conflict with every open PR. Agree on the target structure, migrate in a dedicated PR, and merge it before other work continues.

Sign in to like, dislike, or report.

Feature-first folder structure in Flutter — ANN Tech