| name | flutter-project-structure |
| description | Use this skill whenever a new Flutter/Dart project is being scaffolded, an existing Flutter project is being reorganized, or a new feature is being added to an existing Flutter app. Triggers include "new Flutter app", "start a Flutter project", "where should this file go in Flutter", "add a feature to my Flutter app", "refactor Flutter folder structure", or any mention of `lib/`, `pubspec.yaml`, or feature module organization in Dart code. Use this even when the user doesn't explicitly ask for structure advice — if they're starting a Flutter project or adding a feature, apply this layout by default. |
Flutter Project Structure (Feature-First)
Encodes the preferred folder layout for Flutter apps: feature-first, with shared infrastructure under core/. Optimized for medium-to-large apps with multiple developers and an OpenAPI-generated API client.
Top-level layout
lib/
├── main.dart # ProviderScope + app bootstrap only
├── app.dart # MaterialApp/router config
├── core/ # Cross-cutting infrastructure
│ ├── api/ # Generated OpenAPI client + interceptors
│ ├── auth/ # Token storage, refresh, session
│ ├── config/ # Env, flavors, feature flags
│ ├── error/ # Failure types, error mapping
│ ├── router/ # go_router config, route guards
│ ├── theme/ # ThemeData, colors, typography
│ └── widgets/ # Truly shared widgets (buttons, loaders)
├── features/
│ ├── auth/
│ │ ├── data/ # Repository impl, DTOs, mappers
│ │ ├── domain/ # Entities, repository interface
│ │ ├── presentation/ # Screens, widgets, providers
│ │ └── auth.dart # Barrel export (public API of feature)
│ └── home/
│ └── ...
└── l10n/ # Localization
Rules
-
Features don't import from other features. If feature A needs something from feature B, that something belongs in core/ or in a shared domain/ model. Cross-feature imports are a smell — flag them in review.
-
core/ is for infrastructure, not business logic. If you find yourself adding domain entities to core/, you're probably missing a feature module.
-
One barrel export per feature (features/<name>/<name>.dart) defines the public API. Internal files (providers, widgets, DTOs) are not exported. Other layers consume the feature only through the barrel.
-
presentation/ holds Riverpod providers colocated with the screens that use them. Don't centralize providers in a top-level providers/ folder.
-
Generated code lives next to its source with .g.dart / .freezed.dart suffixes. Add **/*.g.dart and **/*.freezed.dart to analysis_options.yaml excludes for the analyzer (not for git — commit generated code so CI doesn't have to regenerate).
When to deviate
- Tiny apps (<5 screens): skip the
data/domain/presentation split inside features. Just features/<name>/ with files flat. Promote to the full split when the feature grows past ~8 files.
- Packages/plugins: different rules entirely — use standard Dart package layout.
Pubspec conventions
- Pin direct dependencies to caret ranges (
^1.2.3), not any.
- Group dev_dependencies: linting first, codegen second, testing third — improves diff readability.
- Use
dependency_overrides only with a comment explaining why and when to remove it.
Bootstrap checklist for a new project
flutter create --org cr.<company> --platforms=ios,android <name>
- Add
flutter_riverpod, go_router, freezed, json_serializable, build_runner, very_good_analysis (or chosen lint set).
- Create the
core/ and features/ skeleton above; add a placeholder features/home/.
- Wire
ProviderScope in main.dart, MaterialApp.router in app.dart.
- Configure flavors (
dev, staging, prod) via --dart-define or flavor files before adding features.
- Set up the OpenAPI client generation step (see
flutter-api-client skill).
Anti-patterns to reject
- A top-level
lib/screens/ or lib/widgets/ folder collecting everything by type instead of by feature.
- A
lib/utils/ grab bag — split into core/<purpose>/ instead.
- Importing from
package:<app>/features/foo/presentation/... directly instead of via the feature barrel.
- Generated files (
.g.dart) committed without their sources, or excluded from git.