Flutter Flavor Orchestrator
☕ Buy me a coffee on Ko-fi - If this package helps you, consider supporting my work!
Build-time orchestration for Flutter flavors across Android and iOS. Configure environment-specific app identity, native metadata, provisioning files, and resource mappings from a single YAML source.
What's New (v0.7.0)
--output jsonfor all commands — Every command (apply,list,info,validate,plan,rollback) now supports--output jsonfor machine-readable output, enabling robust CI automation.- Stable JSON top-level keys — Each command emits a JSON object with a
predictable
commandkey and command-specific payload keys (see CLI Usage for details per command). apply --output json— Returnscommand,success,flavor,platforms,dry_run,backup_id(when applicable), andconflicts/error(when applicable).- Silent mode — When
--output jsonis active, logger text output is fully suppressed so only valid JSON appears onstdout. FlavorConfig.toJson()/ProvisioningConfig.toJson()— New serialisation methods on the model classes; exported as public API.FlavorOrchestrator.getFlavorInfo()— Returns theFlavorConfigfor a flavor without writing any text output (used byinfo --output json).FlavorOrchestrator.validateConfigurationsDetailed()— Returns per-flavor validation results as aList<Map<String, Object?>>(used byvalidate --output json).OutputFormat/OutputFormatter/ formatter helpers — New shared utilities exported as public API for downstream programmatic use.
Previous: What's New (v0.6.0)
- Conflict detection before
apply— Everyapplyrun now analyzes the execution plan for conflicts before touching any files. Two classes of conflict are detected:- Duplicate destinations — two or more operations target the same output
path (e.g. a
file_mappingsentry writes to the same file as a platform processor). - Overlapping destinations — one destination path is a parent directory
of another (e.g.
lib/andlib/config/app_config.dart).
- Duplicate destinations — two or more operations target the same output
path (e.g. a
apply --force— Pass--forceto override conflict guardrails and apply the flavor anyway (conflicts are logged as warnings).ConflictAnalyzerpublic API — NewConflictAnalyzerclass andConflictReport/ConflictSeveritymodels exported as public API for programmatic conflict checks on anyExecutionPlan.- Conflicts fail fast before any file mutation.
Previous: What's New (v0.5.0)
- Automatic backup before
apply— Every non-dry-runapplynow snapshots all destination files into.ffo/backups/before touching them. Each backup stores file content, pre-apply checksums, and (after a successful apply) post-apply checksums so that manual edits can be detected. rollbackcommand — Restore project files to their pre-apply state:flutter_flavor_orchestrator rollback --latestrollback --id <id>— Restore from a specific backup by its identifier.rollback --force— Override checksum conflicts when files were edited after the last apply.FlavorOrchestrator.rollbackLatest()/rollbackById()— New public API methods for programmatic rollback.
Previous: What's New (v0.4.0)
plancommand — Preview operations without mutating files:flutter_flavor_orchestrator plan --flavor dev--output json— Machine-readable plan output:plan --flavor dev --output jsonFlavorOrchestrator.planFlavor()— New public method returning anExecutionPlanwithout executing it
Previous: What's New (v0.3.0)
- Typed Operation Models —
OperationKind,PlannedOperation,ExecutionPlanadded as public API; each operation is now a first-class immutable value withtoJson()support - Shared Planning Foundation —
FlavorOrchestratorbuilds anExecutionPlanbefore executing any apply; now exposed publicly viaplanFlavor()for theplancommand AssetProcessor.planFileMappings()— generate file-mapping operations without touching the file system (preview without side-effects)
Previous: What's New (v0.2.0)
- Dry-run Apply Mode (
--dry-run) - Execute full apply processing without changing files - Destination Presence Validation - Dry-run validates destination files/directories exist for every write/copy path
- Safer Preflight for CI/CD - Validate flavor application end-to-end before running a real apply
✨ Features
- Flavor management - Configure dev, staging, production, and custom environments
- Cross-platform native updates - Apply changes to Android and iOS project files
- Native file processors - Update AndroidManifest, Gradle/Gradle KTS, Info.plist, and Xcode project settings
- Format preservation - Keep original AndroidManifest indentation and structure
- Provisioning support - Manage
google-services.jsonandGoogleService-Info.plist - File mappings - Copy flavor-specific files and recursive directories
- Atomic directory replacement - Backup/restore-safe replacement of destination directories
- Persistent backup & rollback — Automatic pre-apply snapshots with checksum validation and
rollbackCLI command - Conflict detection — Pre-apply duplicate-target and overlapping-destination guardrails;
--forceoverride - Machine-readable JSON output —
--output jsonfor all commands (list,info,validate,plan,rollback); stable top-level keys for CI automation - YAML-driven configuration - Single declarative config for all flavors
- Typed execution plan -
ExecutionPlan,PlannedOperation,OperationKindmodels withtoJson() - CLI workflow -
apply,plan,rollback,list,info, andvalidatecommands plancommand — Preview operations without file mutations; text and JSON output- Validation and error handling - Pre-checks for config, files, and required fields
- Documentation and examples - Full example project and practical guides
📋 Table of Contents
- Installation
- What's New (v0.7.0)
- Previous: What's New (v0.6.0)
- Previous: What's New (v0.5.0)
- Previous: What's New (v0.4.0)
- Quick Start
- Pub.dev Workflow
- Configuration
- CLI Usage
- What Gets Modified
- Advanced Configuration
- Architecture
- Examples
- Example Project Hints
- API Documentation
- Contributing
- Roadmap
- License
🚀 Installation
Add flutter_flavor_orchestrator to your pubspec.yaml dev dependencies:
dev_dependencies:
flutter_flavor_orchestrator: ^0.7.0
Then run:
flutter pub get
Activate the CLI tool globally (optional):
dart pub global activate flutter_flavor_orchestrator
⚡ Quick Start
1. Create Configuration File
Create a flavor_config.yaml file in your project root:
dev:
bundle_id: com.example.myapp.dev
app_name: MyApp Dev
metadata:
API_URL: https://dev-api.example.com
provisioning:
android_google_services: configs/dev/google-services.json
ios_google_service: configs/dev/GoogleService-Info.plist
production:
bundle_id: com.example.myapp
app_name: MyApp
metadata:
API_URL: https://api.example.com
provisioning:
android_google_services: configs/production/google-services.json
ios_google_service: configs/production/GoogleService-Info.plist
2. Apply a Flavor
# From your project root
flutter pub run flutter_flavor_orchestrator apply --flavor dev
# Alternative (Dart-native invocation)
dart run flutter_flavor_orchestrator apply --flavor dev
3. Build Your App
flutter clean
flutter pub get
flutter build apk # or flutter build ios
That's it! Your app is now configured for the dev flavor.
Pub.dev Workflow
Use this sequence when integrating the package into a real app:
# 1) Install deps
flutter pub get
# 2) Validate configuration before applying
flutter pub run flutter_flavor_orchestrator validate
# 3) Inspect available flavors
flutter pub run flutter_flavor_orchestrator list
# 4) Apply flavor
flutter pub run flutter_flavor_orchestrator apply --flavor dev --verbose
# 5) Build or run
flutter run
Recommended for CI/CD:
- Run
validateas an early pipeline step - Use explicit flavor names (
dev,staging,production) in build jobs - Keep flavor-specific files under
configs/,assets/, andresources/ - Pass
--configto load a YAML file from a secure external path (for example Jenkins workspace/secret mounts)
⚙️ Configuration
Configuration File Location
You can place your flavor configuration in either:
- Dedicated file:
flavor_config.yamlin your project root (recommended) - In pubspec.yaml: Under a
flavor_configsection - External file path: Any YAML file passed at runtime with
--config
External path examples:
flutter_flavor_orchestrator apply --flavor production --config /secure/jenkins/flavor_config.yaml
flutter_flavor_orchestrator validate --config ./ci/flavor_config.yaml
Configuration Options
Each flavor supports the following configuration options:
| Option | Type | Required | Description |
|---|---|---|---|
bundle_id |
String | ✅ | Bundle identifier (iOS) / Package name (Android) |
app_name |
String | ✅ | Display name of the application |
icon_path |
String | ❌ | Path to app icon assets |
metadata |
Map | ❌ | Custom key-value pairs to inject into manifests |
assets |
List | ❌ | Flavor-specific asset paths |
dependencies |
Map | ❌ | Flavor-specific dependency overrides |
provisioning |
Object | ❌ | Provisioning file configuration |
android_min_sdk_version |
Integer | ❌ | Android minimum SDK version |
android_target_sdk_version |
Integer | ❌ | Android target SDK version |
android_compile_sdk_version |
Integer | ❌ | Android compile SDK version |
ios_min_version |
String | ❌ | iOS minimum deployment target |
custom_gradle_config |
Map | ❌ | Custom Gradle configuration snippets |
custom_info_plist_entries |
Map | ❌ | Custom Info.plist entries |
file_mappings |
Map | ❌ | Flavor-specific file/folder copying (source→destination) |
replace_destination_directories |
Boolean | ❌ | Replace existing directories completely (default: false) |
Provisioning Configuration
provisioning:
android_google_services: path/to/google-services.json
ios_google_service: path/to/GoogleService-Info.plist
additional_files:
destination/path: source/path
File Mappings (New in v0.1.7)
Copy flavor-specific files and folders from source to destination paths. Supports both individual files and recursive directory copying:
dev:
bundle_id: com.example.app.dev
app_name: MyApp Dev
# Enable complete directory replacement (optional, default: false)
replace_destination_directories: true
file_mappings:
# Copy individual configuration files
'lib/config/app_config.dart': 'configs/dev/app_config.dart'
'lib/config/constants.dart': 'configs/shared/constants.dart'
# Copy flavor-specific icons
'assets/app_icon.svg': 'assets/icons/dev/app_icon.svg'
# Recursively copy entire directories
'android/app/src/main/res/drawable': 'assets/dev/android/drawables'
'ios/Runner/Assets.xcassets': 'assets/dev/ios/assets'
# Replace entire theme directory (useful with replace_destination_directories)
'lib/theme': 'resources/dev/themes'
production:
bundle_id: com.example.app
app_name: MyApp
replace_destination_directories: true
file_mappings:
'lib/config/app_config.dart': 'configs/production/app_config.dart'
'lib/config/constants.dart': 'configs/shared/constants.dart'
'assets/app_icon.svg': 'assets/icons/production/app_icon.svg'
'lib/theme': 'resources/production/themes'
Features:
- 📁 Copy individual files or entire directory trees recursively
- 🔄 Automatically replaces existing files at destination
- 📂 Creates destination directories if they don't exist
- 🔍 Detailed logging for each file operation
- ⚠️ Skips non-existent source paths with warnings
- 🔙 Full backup and rollback support
Directory Replacement Mode:
When replace_destination_directories: true:
- 🔒 Safe Backup: Existing destination directory is temporarily renamed
- 📋 Copy New: Complete directory tree is copied from source
- ✅ Success: Backup directory is removed
- ❌ Failure: Original directory is automatically restored
This ensures atomic directory replacement - the destination is either completely replaced or left unchanged.
See example/assets/icons/README.md and example/resources/README.md for practical examples.
Complete Example
See example/flavor_config.yaml for a comprehensive configuration example.
🎮 CLI Usage
The package provides a powerful command-line interface:
Apply Command
Apply a flavor configuration to your project:
# Apply to both platforms
flutter_flavor_orchestrator apply --flavor dev
# Apply using an external YAML config file
flutter_flavor_orchestrator apply --flavor production --config /secure/jenkins/flavor_config.yaml
# Apply to Android only
flutter_flavor_orchestrator apply --flavor staging --platform android
# Dry-run apply (execute checks without changing files)
flutter_flavor_orchestrator apply --flavor dev --dry-run
# Apply to iOS only
flutter_flavor_orchestrator apply --flavor production --platform ios
# Override conflict guardrails and apply anyway
flutter_flavor_orchestrator apply --flavor dev --force
# Enable verbose output
flutter_flavor_orchestrator apply --flavor dev --verbose
# Machine-readable JSON result
flutter_flavor_orchestrator apply --flavor dev --output json
Output includes:
file_mappingscount for the selected flavorreplace_destination_directoriesvalue- Mapping details (
destination <- source) when--verboseis enabled - In
--dry-run, all operations are validated and no files are modified
JSON output (--output json) returns a stable object:
command—"apply"success—true/falseflavor— the applied flavor nameplatforms— list of target platformsdry_run— whether dry-run mode was activebackup_id— backup identifier created before the apply (omitted in dry-run)conflicts— list of conflict descriptors if any were detected (omitted when none)error— error message (only present on failure)
Plan Command
Preview the operations that would be performed for a flavor, without mutating any files:
# Preview both platforms (text output)
flutter_flavor_orchestrator plan --flavor dev
# Preview as machine-readable JSON
flutter_flavor_orchestrator plan --flavor dev --output json
# Preview Android-only plan
flutter_flavor_orchestrator plan --flavor staging --platform android
# Preview from an external YAML config
flutter_flavor_orchestrator plan --flavor production --config /secure/jenkins/flavor_config.yaml
Output includes a per-platform section listing each operation with its kind (copyFile, copyDirectory, writeFile, skip), description, and source/destination paths.
JSON output (--output json) returns the serialised ExecutionPlan with stable top-level keys:
flavor— flavor nameplatforms— target platformstotal_operations,active_operations,skipped_operations— operation countsoperations— ordered list of operation objects
Rollback Command
Restore project files to their pre-apply state from a persistent backup created
by the last apply run:
# Rollback to the most recent backup
flutter_flavor_orchestrator rollback --latest
# Rollback to a specific backup by ID (shown in the apply log)
flutter_flavor_orchestrator rollback --id 20260225_194640123_dev
# Force rollback even if files were manually edited after the last apply
flutter_flavor_orchestrator rollback --latest --force
# Rollback and get machine-readable result
flutter_flavor_orchestrator rollback --latest --output json
Every non-dry-run apply automatically creates a snapshot in .ffo/backups/
before touching any files. The backup stores:
- A copy of every destination file before the apply
- SHA-256 checksums of the pre-apply content
- SHA-256 checksums of the post-apply content (for detecting manual edits)
If files have been manually edited after the last apply, rollback will
report a conflict and exit with code 1. Pass --force to override.
JSON output (--output json) returns a stable object:
command—"rollback"success—true/falsebackup_id— identifier of the restored backupflavor— flavor name from the backupfiles_restored— number of files restorednew_paths_removed— number of newly-created paths removed
On failure (no backup found): command, success: false, error.
List Command
List all available flavors:
flutter_flavor_orchestrator list
# List flavors from external YAML
flutter_flavor_orchestrator list --config ./ci/flavor_config.yaml
# Machine-readable JSON output
flutter_flavor_orchestrator list --output json
JSON output (--output json) returns a stable object:
command—"list"count— total number of flavorsflavors— array of objects, each withname,file_mappings_count,replace_destination_directories
Info Command
Display detailed information about a specific flavor:
flutter_flavor_orchestrator info --flavor production
# Inspect a flavor from external YAML
flutter_flavor_orchestrator info --flavor production --config ./ci/flavor_config.yaml
# Machine-readable JSON output
flutter_flavor_orchestrator info --flavor production --output json
JSON output (--output json) returns a stable object:
command—"info"flavor— fully-serialisedFlavorConfigwith all fields includingname,bundle_id,app_name,file_mappings,file_mappings_count,replace_destination_directories, and any optional fields that are set
Validate Command
Validate all flavor configurations:
flutter_flavor_orchestrator validate
# Validate external YAML
flutter_flavor_orchestrator validate --config ./ci/flavor_config.yaml
# Machine-readable JSON output
flutter_flavor_orchestrator validate --output json
JSON output (--output json) returns a stable object:
command—"validate"valid—trueif all flavors are validflavors— array of per-flavor objects, each withname,valid,errors(list of error strings, empty when valid)
All flavors are evaluated regardless of errors — --output json never aborts
early like text mode does.
Help
Display help information:
flutter_flavor_orchestrator --help
🔨 What Gets Modified
Android
When you apply a flavor, the following Android files are automatically updated:
android/app/src/main/AndroidManifest.xml
- ✏️ Package name (
packageattribute) - ✏️ Application label (
android:label) - ✏️ Metadata entries (
<meta-data>tags) - 🎨 Preserves original formatting - Maintains exact indentation, whitespace, and structure
android/app/build.gradle or android/app/build.gradle.kts
- ✏️ Application ID (
applicationId) - ✏️ SDK versions (
minSdkVersion,targetSdkVersion,compileSdkVersion) - ✏️ Custom Gradle configuration
- 🔄 Supports both Groovy (
.gradle) and Kotlin (.gradle.kts) build scripts
android/app/google-services.json
- 📋 Copied from configured path
iOS
ios/Runner/Info.plist
- ✏️ Bundle display name (
CFBundleDisplayName) - ✏️ Bundle identifier (
CFBundleIdentifier) - ✏️ Minimum OS version (
MinimumOSVersion) - ✏️ Custom plist entries
ios/Runner.xcodeproj/project.pbxproj
- ✏️ Product bundle identifier (
PRODUCT_BUNDLE_IDENTIFIER) - ✏️ Deployment target (
IPHONEOS_DEPLOYMENT_TARGET)
ios/Runner/GoogleService-Info.plist
- 📋 Copied from configured path
🏗️ Advanced Configuration
Custom Gradle Configuration
Inject custom Gradle snippets:
custom_gradle_config:
defaultConfig: |
buildConfigField "String", "API_URL", "\"https://api.example.com\""
buildConfigField "boolean", "DEBUG_MODE", "false"
buildTypes: |
release {
shrinkResources true
minifyEnabled true
}
Custom Info.plist Entries
Add custom iOS configuration:
custom_info_plist_entries:
NSAppTransportSecurity:
NSAllowsArbitraryLoads: true
UIBackgroundModes:
- fetch
- remote-notification
ITSAppUsesNonExemptEncryption: false
Metadata Injection
Add custom metadata to both platforms:
metadata:
API_URL: https://api.example.com
API_KEY: your_api_key
FEATURE_FLAG_X: true
MAX_RETRIES: 3
Android: Added as <meta-data> tags in AndroidManifest.xml
iOS: Added as custom entries in Info.plist
🏛️ Architecture
The package follows Clean Architecture principles:
lib/
├── src/
│ ├── models/ # Data models
│ │ ├── execution_plan.dart # Ordered plan of PlannedOperations (v0.3.0)
│ │ ├── flavor_config.dart # FlavorConfig with toJson() (v0.7.0)
│ │ ├── operation_kind.dart # OperationKind enum (v0.3.0)
│ │ ├── planned_operation.dart # Single step descriptor (v0.3.0)
│ │ └── provisioning_config.dart # ProvisioningConfig with toJson() (v0.7.0)
│ ├── processors/ # Platform processors
│ │ ├── android_processor.dart
│ │ ├── asset_processor.dart # planFileMappings() added (v0.3.0)
│ │ └── ios_processor.dart
│ ├── utils/ # Utilities
│ │ ├── backup_manager.dart # Persistent backup/restore (v0.5.0)
│ │ ├── conflict_analyzer.dart # Duplicate/overlap detection (v0.6.0)
│ │ ├── file_manager.dart
│ │ ├── logger.dart # silent mode added (v0.7.0)
│ │ └── output_formatter.dart # OutputFormat, OutputFormatter (v0.7.0)
│ ├── config_parser.dart # Configuration parsing; parseConfigUnchecked() (v0.7.0)
│ └── orchestrator.dart # Main orchestrator; getFlavorInfo(),
│ # validateConfigurationsDetailed(), silent (v0.7.0)
└── flutter_flavor_orchestrator.dart # Public API
Key Components
- FlavorOrchestrator: Coordinates the entire process; builds an
ExecutionPlanbefore applying; runs conflict analysis before any file mutation - ConfigParser: Parses and validates YAML configurations;
parseConfigUnchecked()for per-flavor validation (v0.7.0) - AndroidProcessor: Handles Android-specific modifications
- IosProcessor: Handles iOS-specific modifications
- AssetProcessor: Handles file-mapping copy and planning operations
- ExecutionPlan / PlannedOperation / OperationKind: Typed, immutable operation models with JSON serialisation
- ConflictAnalyzer: Pre-apply conflict detection — duplicate and overlapping destinations (v0.6.0)
- BackupManager: Persistent pre-apply snapshots with checksum validation and rollback
- FileManager: Provides safe file operations with backup/rollback
- OutputFormatter:
typedeffor result-writing functions;textOutputFormatter(no-op),jsonOutputFormatter(stdout JSON),formatterFor(),parseOutputFormat()— all exported as public API (v0.7.0)
📚 Examples
Check out the example directory for a complete working example with:
- ✅ Multiple flavor configurations
- ✅ Firebase integration
- ✅ Custom metadata
- ✅ Platform-specific configurations
- ✅ Complete Flutter app
- ✅ File mappings and safe directory replacement
- ✅ Visual flavor verification in the running UI
Example Project Hints
The example app is designed so you can immediately see flavor changes on screen.
Quick demo in example/
cd example
flutter pub get
# Apply development flavor
flutter pub run flutter_flavor_orchestrator apply --flavor dev --verbose
flutter run
Then switch flavor and run again:
flutter pub run flutter_flavor_orchestrator apply --flavor staging --verbose
flutter run
What to look for in the UI:
- Flavor-specific SVG icon
- Environment/debug banner values
- Color and typography preview from copied theme files
- API/config values from copied
lib/config/app_config.dart
Useful example references:
- Full config: example/flavor_config.yaml
- File mapping assets: example/assets/icons/README.md
- Theme replacement resources: example/resources/README.md
- Gradle syntax notes: example/CUSTOM_GRADLE_CONFIG.md
📖 API Documentation
Programmatic Usage
You can also use the package programmatically in your Dart code:
import 'package:flutter_flavor_orchestrator/flutter_flavor_orchestrator.dart';
void main() async {
final orchestrator = FlavorOrchestrator(
projectRoot: '/path/to/project',
configPath: '/secure/jenkins/flavor_config.yaml',
verbose: true,
);
// Apply a flavor
final success = await orchestrator.applyFlavor(
'dev',
platforms: ['android', 'ios'],
);
// Apply overriding conflict guardrails
final forcedSuccess = await orchestrator.applyFlavor(
'dev',
platforms: ['android', 'ios'],
force: true,
);
// Preview operations without mutating files
final plan = await orchestrator.planFlavor(
'dev',
platforms: ['android', 'ios'],
);
// Check for conflicts before applying
final conflicts = const ConflictAnalyzer().analyze(plan);
for (final conflict in conflicts) {
print('[${conflict.severity.name}] ${conflict.code}: ${conflict.message}');
}
// List flavors
final flavors = await orchestrator.listFlavors();
// Validate configurations
final valid = await orchestrator.validateConfigurations();
}
Core Classes
- FlavorConfig: Represents a complete flavor configuration
- ProvisioningConfig: Provisioning file configuration
- ExecutionPlan: Ordered list of
PlannedOperations for a flavor; serialisable viatoJson() - PlannedOperation: Immutable descriptor of a single orchestration step with kind, paths, and platform
- OperationKind: Enum —
copyFile,copyDirectory,writeFile,skip - ConflictReport: Immutable conflict descriptor with
code,severity,message,conflictingPaths, andtoJson() - ConflictSeverity: Enum —
error,warning - ConflictAnalyzer: Analyses an
ExecutionPlanfor duplicate and overlapping destinations - ConfigParser: Configuration parsing and validation
- FlavorOrchestrator: Main orchestration logic
See the API documentation for detailed class and method documentation.
🔒 Safety Features
Conflict Detection (New in v0.6.0)
Before touching any files, apply scans the execution plan for two classes
of conflict:
- Duplicate destinations — two operations write to the same output path.
Example: a
file_mappingsentry that targetsandroid/app/src/main/AndroidManifest.xmlwhen the Android platform processor already writes to that path. - Overlapping destinations — one destination is a parent directory of
another (e.g.
lib/andlib/config/app_config.dartboth appear as destinations).
If any conflict is found, apply aborts immediately (before any mutation) and
exits with code 1. Pass --force to override and continue despite the
conflicts (conflicts are then logged as warnings).
# Abort on conflict (default)
flutter_flavor_orchestrator apply --flavor dev
# Override conflicts and apply anyway
flutter_flavor_orchestrator apply --flavor dev --force
You can also inspect conflicts programmatically using the public API:
final plan = await orchestrator.planFlavor('dev');
final conflicts = const ConflictAnalyzer().analyze(plan);
for (final c in conflicts) {
print('[${c.severity.name}] ${c.code}: ${c.message}');
}
Automatic Backups
The orchestrator automatically creates backups of all modified files before making changes. If an error occurs, all changes are automatically rolled back.
Validation
All configurations are validated before being applied:
- ✅ Required fields presence
- ✅ Bundle ID format validation
- ✅ File existence checks
- ✅ YAML syntax validation
Error Handling
Comprehensive error handling with clear, actionable error messages:
- 🔴 Missing configuration files
- 🔴 Invalid bundle ID formats
- 🔴 Missing native directories
- 🔴 File operation failures
🧪 Testing
The package includes a comprehensive test suite:
# Run all tests
dart test
# Run with coverage
dart test --coverage
🤝 Contributing
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Built with ❤️ by Alessio La Mantia
- Inspired by the need for better flavor management in Flutter projects
- Uses excellent packages:
args,yaml,xml,path
📞 Support
- 📧 Report issues on GitHub Issues
- 💬 Ask questions on Stack Overflow
- 📖 Read the documentation
🗺️ Roadmap
The full roadmap is in ROADMAP.md. Here is a compact summary of progress and upcoming milestones toward the v1.0.0 stable release.
Released
| Version | Highlights |
|---|---|
| v0.1.x | Initial CLI (apply, list, info, validate), Android/iOS processors, file mappings, dry-run, external config path |
| v0.2.0 | Dry-run destination-presence validation, safer CI preflight |
| v0.3.0 | OperationKind, PlannedOperation, ExecutionPlan typed models · shared _buildExecutionPlan() in orchestrator · AssetProcessor.planFileMappings() |
| v0.4.0 | plan command — preview operations without mutating files; planFlavor() public API; --output json |
| v0.5.0 | rollback command + timestamped backup before every non-dry-run apply; rollbackLatest() / rollbackById() public API |
| v0.6.0 | Conflict detection — duplicate-target and overlapping-destination guardrails; apply --force; ConflictAnalyzer public API |
| v0.7.0 | Automation contract — --output json for list, info, validate, plan, rollback; OutputFormatter public API; FlavorConfig.toJson() |
Upcoming
| Version | Theme | Key deliverable |
|---|---|---|
| v0.8.0 | Schema hardening | schema_version enforcement, validate --strict, migration scaffold |
| v0.9.0 | Diagnostics | doctor command with categorised findings (error/warning/info) |
| v1.0.0 | Stable | Production-ready doctor, docs freeze, no breaking changes without migration path |
| v1.1.0 | Post-1.0 | Env-var interpolation (${VAR:-default}), init command |
Made with ❤️ for the Flutter community
Libraries
- flutter_flavor_orchestrator
- Flutter Flavor Orchestrator