-
Notifications
You must be signed in to change notification settings - Fork 0
Sample App
The sample is a multi-module KMP application demonstrating the full Featured plugin workflow across three independent feature modules, a shared aggregator, and three thin platform shells.
| Module | Role | Flags |
|---|---|---|
:sample:feature-checkout |
Feature module |
new_checkout (local Boolean, default false), checkout_variant (local enum CheckoutVariant, default LEGACY) |
:sample:feature-promotions |
Feature module |
promo_banner_enabled (remote Boolean, default false) |
:sample:feature-ui |
Feature module |
main_button_red (local Boolean, default true), new_feature_section_enabled (local Boolean, default true) |
:sample:shared |
Aggregator + UI | Applies dev.androidbroadcast.featured.application; no flag declarations of its own. Contains Compose UI (FeaturedSample, SampleApp) and SampleViewModel. |
:sample:android-app |
Android shell | Activity; wires DataStoreConfigValueProvider as the local provider and mounts FeatureFlagsDebugScreen. |
:sample:desktop |
JVM shell |
main() entry point; uses InMemoryConfigValueProvider. |
iosApp/ |
iOS shell | Xcode project consuming FeaturedSampleApp.framework (static, produced by :sample:shared). |
Each :sample:feature-* module ships *FlagObservers.kt — public ConfigValues extension functions that expose the module's flags as Flows and suspend setters. UI consumers call these instead of reaching into GeneratedLocalFlags* / GeneratedRemoteFlags* directly.
Example from :sample:feature-checkout (CheckoutFlagObservers.kt):
public fun ConfigValues.newCheckoutFlow(): Flow<Boolean> =
observe(GeneratedLocalFlagsSampleFeatureCheckout.newCheckout).map { it.value }
public fun ConfigValues.checkoutVariantFlow(): Flow<CheckoutVariant> =
observe(GeneratedLocalFlagsSampleFeatureCheckout.checkoutVariant).map { it.value }
public suspend fun ConfigValues.setNewCheckout(value: Boolean) {
override(GeneratedLocalFlagsSampleFeatureCheckout.newCheckout, value)
}SampleViewModel in :sample:shared then consumes these extensions:
val newCheckout: StateFlow<Boolean> =
configValues.newCheckoutFlow()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000L), false):sample:shared declares featuredAggregation dependencies for all three feature modules and wires the generated output directory into commonMain:
// sample/shared/build.gradle.kts
plugins {
id("dev.androidbroadcast.featured.application")
}
dependencies {
featuredAggregation(project(":sample:feature-checkout"))
featuredAggregation(project(":sample:feature-promotions"))
featuredAggregation(project(":sample:feature-ui"))
}
sourceSets.commonMain.get().kotlin.srcDir(
tasks.named("generateFeaturedRegistry").map { it.outputs.files.singleFile.parentFile },
)The plugin generates object GeneratedFeaturedRegistry { val all: List<ConfigParam<*>> } which is passed to FeatureFlagsDebugScreen:
FeatureFlagsDebugScreen(
configValues = configValues,
registry = GeneratedFeaturedRegistry.all,
)- Edit the relevant feature module's
build.gradle.kts— add a declaration insidefeatured { localFlags { ... } }orfeatured { remoteFlags { ... } }. - Add a public observer / setter in
*FlagObservers.ktreferencing the generatedGeneratedLocalFlags*orGeneratedRemoteFlags*object. - If the UI needs it, expose a
StateFlow+ action inSampleViewModel.
For switching providers (DataStore, SharedPreferences, Firebase Remote Config), see Providers.