Monorepo setup for Flutter with Melos
A Flutter monorepo puts multiple packages in one git repository. Melos is the tool that makes this practical — it links local packages together, runs commands across all packages in parallel, and manages versioning with Conventional Commits.
When a monorepo makes sense
- Multiple Flutter apps sharing code (e.g., consumer and admin apps)
- A feature-based architecture where each feature is a separate package
- A shared design system package used by several apps
- Platform plugins with example apps
For a single small app, a monorepo adds complexity without benefit.
Setup
dart pub global activate melos
melos.yaml at the repository root:
name: my_project
packages:
- apps/*
- packages/*
scripts:
# Run across all packages
get:
run: melos exec -- flutter pub get
description: Get dependencies in all packages
test:
run: melos exec --fail-fast -- flutter test
description: Run tests in all packages
analyze:
run: melos exec -- flutter analyze
description: Analyze all packages
gen:
run: melos exec -- flutter pub run build_runner build --delete-conflicting-outputs
description: Run code generation in all packages
# Run only in specific packages
test:unit:
run: melos exec --scope="*_repository,*_service" -- flutter test
description: Run unit tests only
clean:
run: melos exec -- flutter clean
description: Clean all packages
Directory structure
my_project/
apps/
consumer/ # Consumer-facing Flutter app
pubspec.yaml
admin/ # Admin Flutter app
pubspec.yaml
packages/
design_system/ # Shared UI components
pubspec.yaml
auth_repository/ # Auth data layer
pubspec.yaml
product_repository/ # Product data layer
pubspec.yaml
core_network/ # Shared networking
pubspec.yaml
melos.yaml
pubspec.yaml # Root (usually empty or workspace config)
Local package dependencies
In apps/consumer/pubspec.yaml:
dependencies:
design_system:
path: ../../packages/design_system
auth_repository:
path: ../../packages/auth_repository
Melos handles symlinking so packages resolve correctly without publishing to pub.dev.
Running scripts
# Run flutter pub get in all packages
melos get
# Run tests in all packages in parallel
melos test
# Run only in packages matching a pattern
melos test --scope="*_repository"
# Run in packages that changed since main
melos test --diff=origin/main
Versioning with Melos
Melos supports versioning based on Conventional Commits:
# Preview version bumps
melos version --dry-run
# Apply version bumps and update CHANGELOG
melos version
# Publish to pub.dev
melos publish
Commit message format:
feat:→ minor bumpfix:→ patch bumpfeat!:orBREAKING CHANGE:→ major bump
CI with Melos
.github/workflows/ci.yml:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.0'
cache: true
- run: dart pub global activate melos
- run: melos get
- run: melos analyze
- run: melos test
Common pitfalls
Circular dependencies between packages. If auth_repository imports from product_repository and vice versa, you have a cycle. Extract shared types to a core_models package that both depend on.
Not using melos exec --fail-fast. Without --fail-fast, a test failure in one package doesn't stop the run. You get a full summary at the end, but the CI log becomes hard to read. Use --fail-fast for CI, omit for local debugging.
Version skew between packages. If design_system releases a breaking change and auth_repository is not updated, the app will fail to compile. Coordinate version bumps for breaking changes and use melos version to manage them atomically.