Android SDK
Segment membership
This package only evaluates published targeting lists. It has no membership write API and must not hold a Backend application key.
Update members from your backend. Call membership from your backend SDK, not from the Android app. Flow.
Frontend telemetry
Android SDK 1.8.0 includes compact frontend telemetry across Core, Flow, Compose,
and Views. Configure it on TogglyConfig; the same explicit methods are exposed
by TogglyService, Toggly, UseTogglyResult, TogglyState, and
FeatureFlagViewModel.
Use version 1.8.0 or a newer compatible release from Maven Central.
import io.toggly.core.TogglyService
import io.toggly.core.models.TogglyConfig
import io.toggly.core.models.AppStateType
// Run inside a lifecycle-owned coroutine; retain the service until teardown.
val service = TogglyService(TogglyConfig(
appKey = "YOUR_APP_KEY",
enableTelemetry = true,
metricsBaseUrl = "https://metrics.toggly.io",
telemetryFlushIntervalMs = 45_000L
))
service.init()
service.recordUsage("checkout")
service.recordView("checkout", variant = "experiment-a")
service.incrementCounter("orders")
service.setGauge("cart_total", 12.5)
service.flushTelemetry()
service.setAppState(AppStateType.BACKGROUND)
service.dispose()
Core, Flow, Compose, and Views count the effective leaves they actually evaluate. Forward application backgrounding and dispose directly owned services; both paths perform bounded best-effort work. See the frontend telemetry contract.
Feature variants
Set enableVariants = true on TogglyConfig to fetch signed variant
assignments instead of plain booleans, then read a feature's assigned
variant and configuration value with getVariant / getVariantValue:
val config = TogglyConfig(
appKey = "your-app-key",
enableVariants = true
)
val service = TogglyService(config)
service.init()
val variant = service.getVariant("checkout-button-color")
when (variant?.name) {
"blue" -> showBlueButton()
"green" -> showGreenButton()
else -> showDefaultButton() // null: disabled, unassigned, or enableVariants is false
}
val configValue = service.getVariantValue("checkout-button-color") // Any?, or null
getVariant and getVariantValue are also available on the global Toggly
facade. A feature only resolves a variant when it is enabled and has an
assignment; disabled features and features without a variant both return
null. Enabling enableVariants switches the definitions request from
evaluated-signed to evaluated-variants-signed; isFeatureOn and the
other boolean APIs keep working unchanged either way.
Use Toggly's native Android SDK in Kotlin applications with Jetpack Compose or traditional Views.
Grab the printable Kotlin / Android cheat sheet (download PDF) — Compose composables, Views, ViewModel, storage options.
Session identity
Set instanceId in TogglyConfig. From a coroutine, call
service.setIdentity("user-123", "minted-token") or
service.setInstanceId("minted-token"). setInstanceId(null) clears the token;
setIdentity(null) also clears it and returns to the device identity.
Core and Compose identity/flush methods suspend. Views' FeatureFlagViewModel
launches its own coroutine for identity updates, so their return does not await
the context refresh. Its flushTelemetry() is a suspend function. Usage/counter/gauge methods are synchronous. Reuse the
same service across facades. See session attribution.
Overview
The Android SDK is a modular feature flags solution built entirely in Kotlin with coroutines and Flow support. It provides Jetpack Compose composables, traditional View extensions, and multiple storage options.
Modules
| Module | Artifact | Description |
|---|---|---|
| Core | io.toggly:toggly-android-core | Core functionality, storage, and API client (required) |
| Compose | io.toggly:toggly-compose | Jetpack Compose composables and state |
| Views | io.toggly:toggly-views | Android Views, LiveData, and ViewModel support |
| Room | io.toggly:toggly-room | Room database storage adapter |
| DataStore | io.toggly:toggly-datastore | AndroidX DataStore storage adapter |
Requirements
- Android 7.0+ (API level 24+)
- Kotlin 1.9+
- Java 17+
Installation
Enable mavenCentral() in your dependency repositories, then add the dependencies
to your build.gradle.kts. Keep Core and the selected companion modules on the
same version.
dependencies {
// Core module (required)
implementation("io.toggly:toggly-android-core:1.8.0")
// UI modules (pick what you need)
implementation("io.toggly:toggly-compose:1.8.0") // Jetpack Compose
implementation("io.toggly:toggly-views:1.8.0") // Android Views
// Storage modules (pick one, or use built-in SharedPreferences)
implementation("io.toggly:toggly-room:1.8.0") // Room database
implementation("io.toggly:toggly-datastore:1.8.0") // DataStore
}
Quick Start
Initialize the SDK
import io.toggly.core.Toggly
import io.toggly.core.models.TogglyConfig
import io.toggly.core.storage.SharedPreferencesStorage
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Configure with your Toggly app key
Toggly.configure(
config = TogglyConfig(
appKey = "your-app-key",
environment = "Production"
),
storage = SharedPreferencesStorage(this)
)
// Initialize asynchronously
lifecycleScope.launch {
Toggly.shared.init()
}
}
}
Jetpack Compose Usage
import io.toggly.compose.*
@Composable
fun MyScreen() {
// Use rememberFeatureFlag hook
val isNewDashboardEnabled by rememberFeatureFlag("new-dashboard")
if (isNewDashboardEnabled) {
NewDashboardScreen()
} else {
LegacyDashboardScreen()
}
}
// Or use Feature composable
@Composable
fun WelcomeSection() {
Feature("welcome-banner") {
WelcomeBanner()
}
// On and off with negate
Feature("beta-feature") {
BetaFeatureContent()
}
Feature("beta-feature", negate = true) {
StableFeatureContent()
}
}
// Feature gate with multiple features
@Composable
fun AdminSection() {
FeatureGate(
featureKeys = listOf("admin-access", "premium-features"),
requirement = FeatureRequirement.ALL
) {
AdminPanel()
}
}
Android Views Usage
import io.toggly.views.*
class MyActivity : AppCompatActivity() {
private val viewModel: FeatureFlagViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Bind view visibility to feature flag
newFeatureButton.bindToFeatureFlag(
featureKey = "new-feature",
lifecycleOwner = this
)
// Or use LiveData
viewModel.featureFlagLiveData("new-dashboard").observe(this) { isEnabled ->
newDashboardView.visibility = if (isEnabled) View.VISIBLE else View.GONE
}
// Toggle between two views
toggleViews(
featureKey = "new-checkout",
lifecycleOwner = this,
enabledView = newCheckoutView,
disabledView = legacyCheckoutView
)
}
}
Coroutines and Flow Usage
import io.toggly.core.Toggly
import kotlinx.coroutines.flow.collect
class MyViewModel : ViewModel() {
private val toggly = Toggly.shared
init {
// Collect feature flag changes
viewModelScope.launch {
toggly.featureFlagFlow("my-feature").collect { isEnabled ->
_uiState.update { it.copy(featureEnabled = isEnabled) }
}
}
}
// Simple feature check
suspend fun checkFeature() {
val isEnabled = toggly.isFeatureOn("my-feature")
// Use the result
}
// Feature gate with multiple features
suspend fun checkAccess() {
val hasAccess = toggly.evaluateFeatureGate(
featureKeys = listOf("feature-a", "feature-b"),
requirement = FeatureRequirement.ALL
)
}
}
Configuration Options
TogglyConfig(
appKey = "your-app-key",
baseUri = "https://definitions.toggly.io",
environment = "Production",
featureDefaults = mapOf(
"feature-a" to true,
"feature-b" to false
),
refreshInterval = 180_000L,
useSignedDefinitions = false, // ETag / If-None-Match on refresh
verifySignatures = false, // ES256 via JWKS (SignedDefsVerify)
maxSignatureAgeSeconds = null, // optional freshness window
enableLiveUpdates = true,
enableVariants = false // fetch evaluated-variants-signed; enables getVariant/getVariantValue
)
Signed definitions details: Configuration → Signed definitions.
Storage Options
SharedPreferences (Built-in)
import io.toggly.core.storage.SharedPreferencesStorage
Toggly.configure(
config = config,
storage = SharedPreferencesStorage(context, "toggly_prefs")
)
Room Database
import io.toggly.room.createRoomStorage
Toggly.configure(
config = config,
storage = createRoomStorage(context, "toggly.db")
)
DataStore
import io.toggly.datastore.createDataStoreStorage
Toggly.configure(
config = config,
storage = createDataStoreStorage(context)
)
In-Memory (Testing)
import io.toggly.core.storage.MemoryStorage
Toggly.configure(
config = config,
storage = MemoryStorage()
)
Entity context
Pass the screen entity per evaluation — not via setIdentity. See Entity & page context.
Android is a client: local registerContext only.
Toggly.shared.registerContext("Order") { order: Order ->
TogglyEntityContext(
kind = "Order",
key = order.id.toString(),
attributes = mapOf("Status" to order.status),
)
}
val on = Toggly.shared.isFeatureEnabled("OrderBadge", context = order, kind = "Order")
Gates fail closed without context. Identity is user only.
Identity and Targeting
Set user identity for targeted feature flags:
// Set identity
Toggly.shared.setIdentity("user-123")
// Clear identity
Toggly.shared.setIdentity(null)
Events
Listen to SDK events:
viewModelScope.launch {
Toggly.shared.events.collect { event ->
when (event) {
is TogglyEvent.Initialized -> {
// SDK initialized with flags
}
is TogglyEvent.Refreshed -> {
// Flags refreshed from server
}
is TogglyEvent.Error -> {
// Handle error
Log.e("Toggly", "Error: ${event.error}")
}
is TogglyEvent.FeatureChanged -> {
// Feature flag changed
Log.d("Toggly", "${event.featureKey}: ${event.oldValue} -> ${event.newValue}")
}
// ... other events
}
}
}
Offline Support
The SDK automatically caches feature flags for offline use. Choose a persistent storage option (SharedPreferences, Room, or DataStore) to enable offline support:
// Feature flags persist across app restarts
val storage = SharedPreferencesStorage(context)
Toggly.configure(config = config, storage = storage)
// Flags are available immediately from cache
val isEnabled = Toggly.shared.isFeatureOn("my-feature")
Local-Only Mode
Use the SDK without connecting to Toggly.io:
val config = TogglyConfig(
appKey = "", // Empty app key for local-only mode
defaultFlags = mapOf(
"new-dashboard" to true,
"beta-feature" to false
)
)
Toggly.configure(config = config, storage = MemoryStorage())
Debug Information
Get debug information about the SDK state:
val debugInfo = Toggly.shared.getDebugInfo()
Log.d("Toggly", "App Key: ${debugInfo.appKey}")
Log.d("Toggly", "Environment: ${debugInfo.environment}")
Log.d("Toggly", "Identity: ${debugInfo.identity}")
Log.d("Toggly", "Feature Flags: ${debugInfo.featureFlagCount}")
Next Steps
- Learn about Jetpack Compose Integration with composables and state
- Explore Views Support for traditional Android UI
- See Storage Options for different persistence strategies
- Read about Configuration options
- Check Advanced Usage for identity management and events