Jetpack Compose
Integrate Toggly feature flags seamlessly with Jetpack Compose using composables and state.
Installation
Add the Compose module to your build.gradle.kts:
dependencies {
implementation("io.toggly:toggly-android-core:1.0.0")
implementation("io.toggly:toggly-compose:1.0.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