Jetpack Compose
Integrate Toggly feature flags seamlessly with Jetpack Compose using composables and state.
Telemetry and session ownership
Use the existing Core service for frontend telemetry
and session token rotation. Telemetry is on by
default with an app key; configure enableTelemetry = false to opt out. metricsBaseUrl and
telemetryFlushIntervalMs are independent of definition refresh settings.
UI evaluation records checks; record usage and views explicitly from the real
interaction or navigation callback. Forward background lifecycle to the owning
service and dispose directly owned services at teardown. Storage and UI
companions reuse the owner; they do not create additional reporters.
Installation
Use version 1.8.0 or a newer compatible release from Maven Central for the
Android telemetry and session APIs.
Keep Core and the selected companion modules on the same version.
Add the Compose module to your build.gradle.kts:
dependencies {
implementation("io.toggly:toggly-android-core:1.8.0")
implementation("io.toggly:toggly-compose:1.8.0")
}
Setup
Wrap your app content with TogglyProvider:
import io.toggly.compose.TogglyProvider
@Composable
fun App() {
TogglyProvider {
// Your app content
MainScreen()
}
}
Or provide a custom service:
TogglyProvider(service = myTogglyService) {
MainScreen()
}
rememberFeatureFlag
The rememberFeatureFlag composable returns a reactive state that updates when the feature flag changes:
import io.toggly.compose.rememberFeatureFlag
@Composable
fun MyScreen() {
val isNewDashboard by rememberFeatureFlag("new-dashboard")
if (isNewDashboard) {
NewDashboardScreen()
} else {
LegacyDashboardScreen()
}
}
Default Values
Provide a default value while loading:
val isEnabled by rememberFeatureFlag("my-feature", defaultValue = false)
Feature Composable
The Feature composable conditionally renders content based on feature flag state. Set negate = true to render when the feature is off:
import io.toggly.compose.Feature
@Composable
fun WelcomeSection() {
// Show when enabled
Feature("welcome-banner") {
WelcomeBanner()
}
// On and off paths
Feature("new-checkout") {
NewCheckoutFlow()
}
Feature("new-checkout", negate = true) {
LegacyCheckoutFlow()
}
}
Loading State
Handle the loading state explicitly:
Feature(
featureKey = "beta-feature",
loading = { CircularProgressIndicator() }
) {
BetaContent()
}
Feature(
featureKey = "beta-feature",
negate = true,
loading = { CircularProgressIndicator() }
) {
StableContent()
}
FeatureGate Composable
The FeatureGate composable evaluates multiple feature flags together:
import io.toggly.compose.FeatureGate
import io.toggly.core.models.FeatureRequirement
@Composable
fun AdminSection() {
// ALL requirement - all features must be enabled
FeatureGate(
featureKeys = listOf("admin-access", "premium-tier"),
requirement = FeatureRequirement.ALL
) {
AdminPanel()
}
// ANY requirement - at least one feature must be enabled
FeatureGate(
featureKeys = listOf("feature-a", "feature-b", "feature-c"),
requirement = FeatureRequirement.ANY
) {
ExperimentalSection()
}
}
Negation
Negate the gate evaluation:
FeatureGate(
featureKeys = listOf("maintenance-mode"),
requirement = FeatureRequirement.ALL,
negate = true // Show when maintenance-mode is OFF
) {
MainContent()
}
FeatureSwitch Composable
The FeatureSwitch composable provides variant-style on/off slots:
import io.toggly.compose.FeatureSwitch
@Composable
fun PaymentSection() {
FeatureSwitch(
featureKey = "new-payment-flow",
enabled = { NewPaymentFlow() },
disabled = { LegacyPaymentFlow() }
)
}
Prefer two Feature(..., negate = true) calls when you want the same pattern as Next.js / .NET. Use FeatureSwitch when a single switch with default slots is clearer.
useToggly Hook
Access the Toggly service directly for more control:
import io.toggly.compose.useToggly
@Composable
fun SettingsScreen() {
val toggly = useToggly()
LaunchedEffect(Unit) {
// Refresh feature flags
toggly.refresh()
}
// Access current identity
val identity = toggly.currentIdentity
// Get debug info
val debugInfo = toggly.getDebugInfo()
Column {
Text("Identity: ${identity ?: "Anonymous"}")
Text("Flags: ${debugInfo.featureFlagCount}")
Button(onClick = {
toggly.setIdentity("new-user-123")
}) {
Text("Change Identity")
}
}
}
Collecting Feature Flags
Collect all feature flags as state:
import io.toggly.compose.useTogglyService
@Composable
fun DebugScreen() {
val service = useTogglyService()
val flags by service.featureFlags.collectAsState()
LazyColumn {
items(flags.entries.toList()) { (key, value) ->
Text("$key: $value")
}
}
}
Animation Support
Animate feature flag transitions:
@Composable
fun AnimatedFeature() {
val isNewDesign by rememberFeatureFlag("new-design")
AnimatedContent(
targetState = isNewDesign,
transitionSpec = {
fadeIn() togetherWith fadeOut()
}
) { enabled ->
if (enabled) {
NewDesignScreen()
} else {
OldDesignScreen()
}
}
}
Entity context
Feature("OrderBadge", context = order, contextKind = "Order") {
Badge()
}
Entity is per composable. setIdentity is user. Clients do not register schemas. See Entity & page context.
Best Practices
1. Place Providers at Root Level
@Composable
fun App() {
TogglyProvider {
MaterialTheme {
// Your app content
}
}
}
2. Use Meaningful Default Values
// Default to false for new features
val isBetaFeature by rememberFeatureFlag("beta-feature", defaultValue = false)
// Default to true for core functionality
val isSearchEnabled by rememberFeatureFlag("search", defaultValue = true)
3. Handle Loading States
@Composable
fun CriticalFeature() {
val loadStatus by Toggly.shared.loadStatus.collectAsState()
when (loadStatus) {
TogglyLoadStatus.IDLE,
TogglyLoadStatus.LOADING -> LoadingScreen()
TogglyLoadStatus.LOADED,
TogglyLoadStatus.ERROR -> {
// Use feature flags
val isEnabled by rememberFeatureFlag("critical-feature")
// ...
}
}
}
4. Test with Preview
@Preview
@Composable
fun PreviewFeatureOn() {
// Create a mock provider for previews
val mockService = MockTogglyService(
flags = mapOf("my-feature" to true)
)
TogglyProvider(service = mockService) {
MyScreen()
}
}
Next Steps
- Learn about Views Support for traditional Android UI
- Explore Storage Options for persistence
- Check Advanced Usage for more patterns