-
Notifications
You must be signed in to change notification settings - Fork 0
Declaring Flags
Flags are declared in the module's build.gradle.kts using the featured { } DSL block. The Gradle plugin reads the declarations and generates typed Kotlin code — no runtime reflection, no string keys in application code.
// build.gradle.kts
featured {
localFlags {
boolean("new_checkout", default = false) {
description = "Enable the new checkout flow"
category = "Checkout"
}
int("max_cart_items", default = 10) {
description = "Maximum items allowed in cart"
}
}
remoteFlags {
boolean("promo_banner", default = false) {
description = "Show promo banner (remote-controlled)"
}
}
}localFlags declares flags whose values come from a local provider (DataStore, SharedPreferences, etc.) or fall back to the declared default. remoteFlags declares flags fetched from a remote source (e.g., Firebase Remote Config).
| DSL function | Kotlin type | Generated accessor |
|---|---|---|
boolean(key, default = …) |
Boolean |
fun ConfigValues.isXxxEnabled(): Boolean (for boolean), or fun ConfigValues.getXxx(): ConfigValue<Boolean>
|
int(key, default = …) |
Int |
fun ConfigValues.getXxx(): Int |
enum(key, typeFqn, default) |
user-defined Enum<T>
|
fun ConfigValues.getXxx(): T |
Other types may be available — check the plugin's DSL for the full list. The documented types above are stable.
Declaring an enum flag generates a typed ConfigParam<E>, but at runtime every storage-backed local provider must have an enumConverter<E>() registered via registerConverter(...) before the first read or write. Otherwise the provider throws IllegalArgumentException synchronously.
This applies to:
DataStoreConfigValueProviderJavaPreferencesConfigValueProviderSharedPreferencesProviderConfig
FirebaseConfigValueProvider handles enums automatically via reflection — no registration needed.
NSUserDefaultsConfigValueProvider does not support enums at this time — it has no converter API. See Known Limitations for the iOS workaround.
Example:
// Gradle DSL — declaration
featured {
localFlags {
enum(
key = "checkout_variant",
typeFqn = "com.example.CheckoutVariant",
default = "LEGACY",
)
}
}// Runtime — required wiring for non-Firebase local providers
val provider = DataStoreConfigValueProvider(dataStore).apply {
registerConverter(enumConverter<CheckoutVariant>())
}
val configValues = ConfigValues(localProvider = provider)Inside the flag block, the following fields are optional:
-
description— human-readable description, shown in the debug UI -
category— groups related flags in the debug UI -
expiresAt— ISO date string; used by lint/Detekt rules to flag stale declarations
For a boolean flag named new_checkout, the plugin generates:
// Generated — do not edit
internal object GeneratedLocalFlags {
val newCheckout: ConfigParam<Boolean> = ConfigParam(
key = "new_checkout",
defaultValue = false,
)
}
// Public extension on ConfigValues
fun ConfigValues.isNewCheckoutEnabled(): Boolean =
getValue(GeneratedLocalFlags.newCheckout).valueFor an int flag named max_cart_items:
internal object GeneratedLocalFlags {
val maxCartItems: ConfigParam<Int> = ConfigParam(
key = "max_cart_items",
defaultValue = 10,
)
}
fun ConfigValues.getMaxCartItems(): Int =
getValue(GeneratedLocalFlags.maxCartItems).valueRemote flags generate getXxx(): ConfigValue<T> extensions that expose both the value and its source (DEFAULT, LOCAL, or REMOTE).
Flag keys use snake_case in the DSL. The plugin converts them to camelCase for the generated Kotlin identifiers. The raw key string is used as-is when reading from providers and when generating xcconfig conditions for iOS.
localFlags declarations are eligible for dead-code elimination in release builds — the plugin generates R8 -assumevalues rules for every boolean local flag with default = false. remoteFlags values are dynamic (fetched at runtime) and are never eligible for DCE. See Release Optimization for details.