
Type-safe, reactive feature-flag and configuration manager with generated typed helpers, runtime local/remote providers, Compose-friendly observers, debug UI, and build-time dead-code elimination for disabled flags.
Featured is a type-safe, reactive feature-flag and configuration management library for Kotlin Multiplatform — Android, iOS (via SKIE), and JVM.
ConfigValues. No string keys, no unchecked casts.default = false makes the guarded code unreachable. The Gradle plugin emits R8 -assumevalues rules (Android/JVM) and an xcconfig with DISABLE_<FLAG> Swift compilation conditions (iOS), so the respective compilers physically strip disabled branches from release binaries.Flow; Compose and SwiftUI/Combine integrations included.| Platform | Status |
|---|---|
| Android | Stable |
| iOS (SKIE / DCE) | Preview |
| JVM | Preview |
Preview means the platform is functional but its public API may change in minor releases without a major version bump. Stable platforms follow Semantic Versioning.
// build.gradle.kts — declare the flag
plugins {
id("dev.androidbroadcast.featured") version "<version>"
}
dependencies {
implementation(platform("dev.androidbroadcast.featured:featured-bom:<version>"))
implementation("dev.androidbroadcast.featured:featured-core")
implementation("dev.androidbroadcast.featured:featured-datastore-provider")
}
featured {
localFlags {
boolean("new_checkout", default = false) {
description = "Enable the new checkout flow"
}
}
}// Application.kt — wire up ConfigValues once
val dataStore = PreferenceDataStoreFactory.create { context.dataStoreFile("feature_flags.preferences_pb") }
val configValues = ConfigValues(
localProvider = DataStoreConfigValueProvider(dataStore),
)// Read the generated extension anywhere
val isEnabled: Boolean = configValues.isNewCheckoutEnabled()In a multi-module app, construct one ConfigValues per feature module plus one debug aggregator,
all sharing the same LocalConfigValueProvider:
// Construct one ConfigValues per feature module + one debug aggregator, all over a shared provider
val sharedLocal: LocalConfigValueProvider = defaultLocalProvider(applicationContext)
val checkoutConfig = ConfigValues(localProvider = sharedLocal)
val promotionsConfig = ConfigValues(localProvider = sharedLocal)
val uiConfig = ConfigValues(localProvider = sharedLocal)
// Debug-only aggregator that the FeatureFlagsDebugScreen drives
val debugConfig = ConfigValues(localProvider = sharedLocal)
FeatureFlagsDebugScreen(
configValues = debugConfig,
registry = GeneratedFeaturedRegistry.all,
)Each feature module owns its own ConfigValues and observes only its own flags (via public
observe-bridge extensions). The generated GeneratedLocalFlagsX / GeneratedRemoteFlagsX objects
are internal to their module — cross-module flag listing flows exclusively through
GeneratedFeaturedRegistry.all, which is built from the per-module manifests by the aggregator
plugin. The single source of truth for stored overrides is the shared LocalConfigValueProvider,
so writes from any instance propagate to every other one through its reactive observe flow.
Full documentation lives in the Wiki:
See CONTRIBUTING.md.
See SECURITY.md.
MIT — see LICENSE.
Featured is a type-safe, reactive feature-flag and configuration management library for Kotlin Multiplatform — Android, iOS (via SKIE), and JVM.
ConfigValues. No string keys, no unchecked casts.default = false makes the guarded code unreachable. The Gradle plugin emits R8 -assumevalues rules (Android/JVM) and an xcconfig with DISABLE_<FLAG> Swift compilation conditions (iOS), so the respective compilers physically strip disabled branches from release binaries.Flow; Compose and SwiftUI/Combine integrations included.| Platform | Status |
|---|---|
| Android | Stable |
| iOS (SKIE / DCE) | Preview |
| JVM | Preview |
Preview means the platform is functional but its public API may change in minor releases without a major version bump. Stable platforms follow Semantic Versioning.
// build.gradle.kts — declare the flag
plugins {
id("dev.androidbroadcast.featured") version "<version>"
}
dependencies {
implementation(platform("dev.androidbroadcast.featured:featured-bom:<version>"))
implementation("dev.androidbroadcast.featured:featured-core")
implementation("dev.androidbroadcast.featured:featured-datastore-provider")
}
featured {
localFlags {
boolean("new_checkout", default = false) {
description = "Enable the new checkout flow"
}
}
}// Application.kt — wire up ConfigValues once
val dataStore = PreferenceDataStoreFactory.create { context.dataStoreFile("feature_flags.preferences_pb") }
val configValues = ConfigValues(
localProvider = DataStoreConfigValueProvider(dataStore),
)// Read the generated extension anywhere
val isEnabled: Boolean = configValues.isNewCheckoutEnabled()In a multi-module app, construct one ConfigValues per feature module plus one debug aggregator,
all sharing the same LocalConfigValueProvider:
// Construct one ConfigValues per feature module + one debug aggregator, all over a shared provider
val sharedLocal: LocalConfigValueProvider = defaultLocalProvider(applicationContext)
val checkoutConfig = ConfigValues(localProvider = sharedLocal)
val promotionsConfig = ConfigValues(localProvider = sharedLocal)
val uiConfig = ConfigValues(localProvider = sharedLocal)
// Debug-only aggregator that the FeatureFlagsDebugScreen drives
val debugConfig = ConfigValues(localProvider = sharedLocal)
FeatureFlagsDebugScreen(
configValues = debugConfig,
registry = GeneratedFeaturedRegistry.all,
)Each feature module owns its own ConfigValues and observes only its own flags (via public
observe-bridge extensions). The generated GeneratedLocalFlagsX / GeneratedRemoteFlagsX objects
are internal to their module — cross-module flag listing flows exclusively through
GeneratedFeaturedRegistry.all, which is built from the per-module manifests by the aggregator
plugin. The single source of truth for stored overrides is the shared LocalConfigValueProvider,
so writes from any instance propagate to every other one through its reactive observe flow.
Full documentation lives in the Wiki:
See CONTRIBUTING.md.
See SECURITY.md.
MIT — see LICENSE.