flavors-and-flags
Skill0 starsChanged 52 days agoMITView on GitHub
Explains how the dev/prod flavor system works end to end across AppEnvironment, Android product flavors, iOS xcconfigs and .vscode/launch.json, and how to add a new per-flavor FeatureFlag; load before adding a feature flag, changing a flavor-specific default, or touching flavor/build-flag wiring.
In this repository35 other files it gives its agents.
- AGENTS.mdAGENTS.md
- bootstrap-projectSkill
- choose-data-stackSkill
- code-reviewSkill
- conventional-commitsSkill
- data-providerSkill
- domain-modelingSkill
- error-handlingSkill
- flutter-fvmSkill
- localizationSkill
- new-featureSkill
- quality-gateSkill
- routingSkill
- state-managementSkill
- testingSkill
- themingSkill
- ui-screenSkill
- bootstrap-projectSkill
- choose-data-stackSkill
- code-reviewSkill
- conventional-commitsSkill
- data-providerSkill
- domain-modelingSkill
- error-handlingSkill
- flavors-and-flagsSkill
- flutter-fvmSkill
- flutter_fvmSkill
- localizationSkill
- new-featureSkill
- quality-gateSkill
- routingSkill
- state-managementSkill
- testingSkill
- themingSkill
- ui-screenSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 70
Use .vscode/launch.json's six configurations instead — they pair every flavor with every run mode:
- Distinctive: line 82
If you must run from the terminal instead of the VS Code/editor launch UI, copy the pair of flags from the matching configuration verbatim, …
- Distinctive: line 89
Never type only one of the two flags, and never let a script default one of them independently of the other.
- Very specific: line 182
If you need any of that, the extension point is FeatureFlagRepository's backing …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Flavors and feature flags
1. How the flavor system works end to end
There are exactly two build flavors: dev and prod. Three things must
always agree, and the template wires them so that using
.vscode/launch.json is the only way you need to think about this:
--dart-define=FLAVOR=<dev|prod>— read once bylib/app_environment.dart:enum AppFlavor { dev, prod } abstract final class AppEnvironment { static const String _name = String.fromEnvironment('FLAVOR', defaultValue: 'dev'); static AppFlavor get flavor => _name == 'prod' ? AppFlavor.prod : AppFlavor.dev; }This is a compile-time constant (
String.fromEnvironment), soAppEnvironment.flavoris available anywhere in Dart code without plumbing it through constructors —lib/bootstrap.dartreads it once to pick feature-flag defaults (see section 2).--flavor <dev|prod>— the native flag. On Android it selects the GradleproductFlavordeclared inandroid/app/build.gradle.kts:flavorDimensions += "env" productFlavors { create("dev") { dimension = "env" applicationIdSuffix = ".dev" versionNameSuffix = "-dev" resValue(type = "string", name = "app_name", value = "Flutter Agentic Template Dev") } create("prod") { dimension = "env" resValue(type = "string", name = "app_name", value = "Flutter Agentic Template") } }On iOS it selects the Xcode scheme/configuration, which includes
ios/Flutter/flavor-dev.xcconfigorflavor-prod.xcconfig— per-flavor overrides for the bundle id suffix and display name:// flavor-dev.xcconfig APP_BUNDLE_ID_SUFFIX=.dev APP_DISPLAY_NAME=Flutter Agentic Template Dev- Both flags are required on every
flutter run/flutter build, and must always agree. A--flavor prodbuild carrying--dart-define=FLAVOR=devinstalls as the production app (prod bundle id, prod signing, prod app name) whileAppEnvironment.flavor— and therefore every feature-flag default and the(Dev)title suffix — reports itself asdev. This is a real, silent-failure-mode footgun, which is why the template does not want you typing these flags by hand.
Use .vscode/launch.json's six configurations instead — they pair every
flavor with every run mode:
| Configuration | --flavor | --dart-define=FLAVOR | Mode |
| Dev (Debug) | dev | dev | debug |
| Dev (Profile) | dev | dev | profile |
| Dev (Release) | dev | dev | release |
| Prod (Debug) | prod | prod | debug |
| Prod (Profile) | prod | prod | profile |
| Prod (Release) | prod | prod | release |
If you must run from the terminal instead of the VS Code/editor launch UI, copy the pair of flags from the matching configuration verbatim, e.g.:
fvm flutter run --flavor dev --dart-define=FLAVOR=devNever type only one of the two flags, and never let a script default one of them independently of the other.
2. Adding a new FeatureFlag
FeatureFlag (packages/domain/lib/src/entities/feature_flag.dart) is a
small, closed enum — one value per toggle:
enum FeatureFlag {
exampleFeature,
}To add a flag, e.g. a taskReminders feature still being finished:
Add the enum value in
packages/domain/lib/src/entities/feature_flag.dart:enum FeatureFlag { exampleFeature, taskReminders, }Set its per-flavor default in
lib/feature_flag_defaults.dart(repo root). This is the single place flavor and flag intersect — it's a pureMapliteral perAppFlavor, called once frombootstrap():Map<FeatureFlag, bool> featureFlagDefaultsFor(AppFlavor flavor) => switch (flavor) { AppFlavor.dev => { FeatureFlag.exampleFeature: true, FeatureFlag.taskReminders: true, // on in dev while the feature is finished }, AppFlavor.prod => { FeatureFlag.exampleFeature: false, FeatureFlag.taskReminders: false, // hidden from real users until it's done }, };A flag doesn't have to differ per flavor — plenty of flags are simply "on everywhere" or "off everywhere" and only exist so a Cubit can gate on them without a code change later:
Map<FeatureFlag, bool> featureFlagDefaultsFor(AppFlavor flavor) => switch (flavor) { AppFlavor.dev => { FeatureFlag.exampleFeature: true, FeatureFlag.taskReminders: true, FeatureFlag.darkModeToggle: true, // same value in both flavors }, AppFlavor.prod => { FeatureFlag.exampleFeature: false, FeatureFlag.taskReminders: false, FeatureFlag.darkModeToggle: true, // same value in both flavors }, };Read it in UI via the app-wide
FeatureFlagCubit(provided once inlib/app.dart, aboveMaterialApp.router):Widget _buildReminderBanner(BuildContext context) { final remindersEnabled = context.watch<FeatureFlagCubit>().state.isEnabled( FeatureFlag.taskReminders, ); return remindersEnabled ? const ReminderBanner() : const SizedBox.shrink(); }context.watch<FeatureFlagCubit>()rebuilds the widget if the underlying state ever changes; usecontext.read<FeatureFlagCubit>()instead inside a one-off callback (e.g. inside a Cubit method or anonPressed) where a rebuild isn't needed.
A flag not present in the underlying map defaults to false (see
FeatureFlags.isEnabled in feature_flag.dart), so adding a new
FeatureFlag value is never a breaking change for code that reads an
older flag.
3. What this system is not
FeatureFlag/FeatureFlagRepository is a small, closed, build-time
toggle — values are fixed at process start from
featureFlagDefaultsFor(AppEnvironment.flavor) and never change again
during that run. This is not a remote-config or A/B-testing system,
by design: no server call, no per-user targeting, no runtime rollout
percentage, no over-the-air update without a new build.
If you need any of that, the extension point is
FeatureFlagRepository's backing (packages/domain/lib/src/repositories/feature_flag_repository.dart)
— today it's constructed directly from a FeatureFlags map built at
startup:
final featureFlagRepository = FeatureFlagRepository(
FeatureFlags(featureFlagDefaultsFor(AppEnvironment.flavor)),
);Replacing this with a data-layer remote-config provider (a new
data_provider interface in domain, implemented against your chosen
backend in data, injected the same way every other repository is in
lib/bootstrap.dart) is the correct place to add that capability. The
FeatureFlag enum and FeatureFlagCubit/FeatureFlagState in
package:ui would not need to change — this skill does not implement
that provider; see .agents/skills/choose-data-stack/SKILL.md and
.agents/skills/data-provider/SKILL.md when you actually need it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

