Managing a Flutter monorepo with Melos
By John · 4 March 2026
A Flutter monorepo puts multiple packages and apps in one Git repository. The benefit: a single PR can span shared library code and the app that uses it, tests run together, and versioning is coordinated. Melos is the tool that makes Flutter monorepos manageable.
When a monorepo makes sense
- Multiple apps sharing significant common code (design system, data layer)
- Internal packages that shouldn't be published to pub.dev
- Teams where the same people work on both the library and the app
- Avoiding the "publish + update pubspec + PR" cycle for every internal change
Project structure
my_workspace/
├── melos.yaml
├── apps/
│ ├── customer_app/
│ └── admin_app/
├── packages/
│ ├── core/ # Business logic, models, repositories
│ ├── ui/ # Design system, shared widgets
│ ├── api/ # HTTP client, DTOs
│ └── analytics/ # Analytics abstraction
└── tools/
└── scripts/
melos.yaml
name: my_workspace
packages:
- apps/**
- packages/**
command:
bootstrap:
# Use local paths for workspace packages
usePubspecOverrides: true
scripts:
test:all:
run: melos exec -- flutter test
description: Run tests in all packages
packageFilters:
dirExists: test
test:coverage:
run: melos exec -- flutter test --coverage
packageFilters:
dirExists: test
analyze:
run: melos exec -- flutter analyze
description: Analyze all packages
gen:
run: melos exec -- dart run build_runner build --delete-conflicting-outputs
description: Run code generation in all packages
packageFilters:
dependsOn: build_runner
clean:
run: melos exec -- flutter clean
description: Clean all packages
format:
run: melos exec -- dart format . --fix
# Run tests only in changed packages (useful in CI)
test:changed:
run: melos exec -- flutter test
packageFilters:
dirExists: test
diff: origin/main
Bootstrap
npm install -g melos # Or: dart pub global activate melos
melos bootstrap
# Equivalent to: flutter pub get in every package
# Also sets up pubspec_overrides.yaml so packages reference each other locally
Using local packages
With usePubspecOverrides: true, Melos creates pubspec_overrides.yaml in each package automatically. Your pubspec.yaml can reference the published version:
# apps/customer_app/pubspec.yaml
dependencies:
core: ^1.0.0
ui: ^1.0.0
And Melos overrides it to use the local path during development:
# apps/customer_app/pubspec_overrides.yaml (generated, gitignored)
dependency_overrides:
core:
path: ../../packages/core
ui:
path: ../../packages/ui
Running scripts
# Run across all packages
melos run test:all
# Run in specific package
melos run test:all --scope=core
# Run in changed packages only
melos run test:changed
# Run in packages that depend on 'core'
melos run test:all --scope='*' --filter='dependsOn:core'
# Exec directly (without a named script)
melos exec -- flutter pub outdated
CI workflow
# .github/workflows/test.yml
name: Test
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for diff-based filtering
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.0'
cache: true
- name: Install Melos
run: dart pub global activate melos
- name: Bootstrap
run: melos bootstrap
- name: Analyze
run: melos run analyze
- name: Test changed packages
run: melos run test:changed
Versioning with Melos
Melos can manage versions across packages:
# Bump version based on conventional commits
melos version
# Publish all changed packages to pub.dev
melos publish
# melos.yaml
command:
version:
# Use conventional commits to determine version bumps
message: "chore(release): publish packages\n\n{new_package_versions}"
includeScopes: true
branch: main
publish:
gitTagVersion: true
Workspace-level analysis
Create an analysis_options.yaml at the workspace root and import it in each package:
# analysis_options.yaml (workspace root)
include: package:flutter_lints/flutter.yaml
analyzer:
errors:
missing_required_param: error
dead_code: warning
linter:
rules:
prefer_const_constructors: true
avoid_print: true
require_trailing_commas: true
# packages/core/analysis_options.yaml
include: ../../analysis_options.yaml
Common pitfalls
Not gitignoring pubspec_overrides.yaml. Add **/pubspec_overrides.yaml to .gitignore. These are local overrides for development — committing them breaks CI which expects published versions.
Circular dependencies. If ui depends on core and core depends on ui, Melos can't bootstrap. Keep dependency direction one-way: apps → ui → core → api.
Missing fetch-depth: 0 in CI. Melos diff-based filtering compares against origin/main. Without full git history, every package looks changed. Always checkout with fetch-depth: 0 when using diff filters.
Running flutter pub get instead of melos bootstrap. In a Melos workspace, melos bootstrap sets up the pubspec overrides. Running flutter pub get directly in a package skips this step.
Sign in to like, dislike, or report.