Skip to content

Monorepo Toolkit

Flavor Manager ​

The flavor command manages app flavors (build variants) using flutter_flavorizr. It handles flavor configuration, native platform setup (Android product flavors, iOS schemes), IDE configurations, and entry point generation.

Switching flavors and regenerating environment config

Subcommands ​

flavor list-apps ​

Lists all apps in the workspace that have flavor configurations:

bash
dart run monorepo_toolkit flavor list-apps

flavor list ​

Lists all configured flavors for a specific app:

bash
dart run monorepo_toolkit flavor list --app template_app

Displays each flavor with its app name, Android application ID, and iOS bundle ID.

flavor add ​

Adds a new flavor to an app:

bash
# Basic usage
dart run monorepo_toolkit flavor add --app template_app --name qa

# With custom app name suffix and bundle ID suffix
dart run monorepo_toolkit flavor add \
  --app template_app \
  --name qa \
  --app-name "[QA]" \
  --suffix ".qa"

# Add and immediately generate native files
dart run monorepo_toolkit flavor add --app template_app --name qa --generate
FlagDescription
--app / -aThe app name (required)
--name / -nThe flavor name, e.g., qa, uat, demo (required)
--app-nameCustom app name suffix. Defaults to [FLAVOR_NAME]
--suffix / -sBundle ID suffix. Defaults to .<flavor_name>. The iOS bundle ID drops underscores from it (.my_flavor becomes .myflavor), because Apple does not allow them
--generate / -gRun flutter_flavorizr after adding

Flavor names must start with a lowercase letter and contain only lowercase letters, numbers, and underscores.

What one run changes:

flavor managerOne command adds a flavor to the config and your IDE

Step 1 of 3, before: The app has development, staging and production. flavorizr.yaml is the source of truth, and .vscode/launch.json has one launch entry per flavor.

dart run monorepo_toolkit flavor list --app template_appℹ Flavors for template_app:ℹ   developmentℹ     App name: Template App [Dev]ℹ     Android ID: com.banuacoder.templateapp.devℹ     iOS Bundle: com.banuacoder.templateapp.devℹ   …ℹ   productionℹ     App name: Template App
apps/template_app/flavorizr.yaml
flavors:  # development, staging …  production:    app:      name: "Template App"    android:      flavorDimension: "app"      applicationId: "com.banuacoder.templateapp"      icon: "launcher/logo.png"      adaptiveIcon:        background: "launcher/logo-background.png"        foreground: "launcher/logo-foreground.png"    ios:      bundleId: "com.banuacoder.templateapp"      icon: "launcher/ios-logo.png"instructions:  # …
.vscode/launch.json
{  "version": "0.2.0",  "configurations": [    // development …    {      "name": "Template App - Production",      "program": "${workspaceFolder}/apps/template_app/lib/main.dart",      // …    },    // staging …  ]}

flavor generate ​

Runs flutter_flavorizr to generate native platform files for all configured flavors:

bash
dart run monorepo_toolkit flavor generate --app template_app

# Force regeneration even if inputs haven't changed
dart run monorepo_toolkit flavor generate --app template_app --force

# Check which flavors need regeneration without making changes
dart run monorepo_toolkit flavor generate --app template_app --check

The generate command runs up to four steps:

  1. flutter_flavorizr for native Android/iOS flavor files and launcher icons
  2. Native splash screen generation (if configured)
  3. IDE configuration generation (see IDE run configurations)
  4. Flavor-based main_<flavor>.dart entry point files

Each step can be toggled with --no-splash, --no-icons, --no-ide-configs, or --no-main-files.

Step 4 creates only the missing entry files. Each one is a copy of lib/main.dart (or another main_<flavor>.dart) with the bootstrap(flavor: ...) argument changed, so it uses your real bootstrap and app widget. Existing files are not changed. For a new flavor, run env sync first so FlavorStatus has the new value.

flavor sync ​

Synchronizes flavor settings between the global flavors.yaml config and individual app configurations:

bash
# Initialize global settings from existing app configs
dart run monorepo_toolkit flavor sync --init

# Sync global settings to all apps (add missing flavors)
dart run monorepo_toolkit flavor sync

# Sync a specific app only
dart run monorepo_toolkit flavor sync --app template_app

# Sync from app configs back to global settings
dart run monorepo_toolkit flavor sync --from-apps

IDE run configurations ​

Every generated workspace keeps its run configurations in one place per IDE, at the workspace root:

IDEFilesCommitted
VS Code.vscode/launch.json, .vscode/tasks.jsonYes (.gitignore keeps these two, ignores the rest of .vscode/)
Android Studio / IntelliJ.run/<app>_<flavor>[_profile|_release].run.xml, .run/prepare-<flavor>.run.xmlYes (.idea/ stays ignored)

Each app in flavors.yaml gets <App> - <Flavor> for every flavor, <App> - <Flavor> (Profile) for development and staging, and <App> - <Flavor> (Release) for staging and production. Each one runs apps/<app>/lib/main_<flavor>.dart (main.dart for production) with --flavor <flavor>, after the shared prepare-<flavor> task (dart run monorepo_toolkit prepare --flavor <flavor>: env switch, envied regeneration and build-prepare).

Three writers produce the same files: flutter_modular_monorepo for the first app, app_scaffold for each new app, and flavor generate/flavor add here. They add only missing entries and files, so a second app merges in and your own entries and edits stay. A launch.json or tasks.json that is not a plain JSON object (comments, an array) is left unchanged, with a warning; when tasks.json is, launch.json is left alone too, because its entries run those tasks. template_base's committed files are the reference, and tests in shared_hooks and flavor_manager check both writers against them.

Projects generated before flutter_modular_monorepo 5.1.0 had un-namespaced Development/Staging/Production configs in .idea/runConfigurations/ (gitignored, so never shared). app_scaffold 2.1.0 and later removes them and their launch.json entries when they are unmodified, for whichever app they were written for, and keeps edited ones with a note to move them to .run/. It also upgrades in place the unmodified entries app_scaffold 2.0.0 and earlier wrote (a lib/... program without a preLaunchTask, and .run/ files with a buildMode option and no prepare step). flavor generate and flavor add do not upgrade them: they only add what is missing.

Android/iOS mapping ​

Each flavor maps to platform-specific identifiers:

PlatformWhat it maps toExample
AndroidapplicationId suffix in build.gradlecom.example.app.staging
iOSBundle identifier in Xcode schemecom.example.app.staging

The generate command creates all necessary native files: Android productFlavors blocks, iOS build configurations, and Xcode schemes.

Default flavors ​

When initializing with flavor sync --init, the default flavors are:

  • development (suffix .development)
  • staging (suffix .staging)
  • production (no suffix)

Workflow ​

  1. Define flavors globally in flavors.yaml
  2. Run dart run monorepo_toolkit flavor sync --init to create the global config
  3. Add new flavors with dart run monorepo_toolkit flavor add
  4. Generate native files with dart run monorepo_toolkit flavor generate
  5. Run the app with flutter run --flavor development

Melos shortcuts ​

bash
melos run flavor:list       # List flavors for an app
melos run flavor:add        # Add a new flavor
melos run flavor:generate   # Generate native flavor files
melos run flavor:sync       # Sync global and app configs