Skip to main content

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.

Quick reference

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​

ModuleArtifactDescription
Coreio.toggly:toggly-android-coreCore functionality, storage, and API client (required)
Composeio.toggly:toggly-composeJetpack Compose composables and state
Viewsio.toggly:toggly-viewsAndroid Views, LiveData, and ViewModel support
Roomio.toggly:toggly-roomRoom database storage adapter
DataStoreio.toggly:toggly-datastoreAndroidX 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​