Android Views
Integrate Toggly feature flags with traditional Android Views using LiveData, ViewModel, and View extensions.
Installation
Add the Views module to your build.gradle.kts:
dependencies {
implementation("io.toggly:toggly-android-core:1.0.0")
implementation("io.toggly:toggly-views:1.0.0")
}
FeatureFlagViewModel
The FeatureFlagViewModel provides LiveData-based access to feature flags:
import io.toggly.views.FeatureFlagViewModel
class MyActivity : AppCompatActivity() {
private val viewModel: FeatureFlagViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize if not already done
viewModel.initialize()
// Observe a single feature flag
viewModel.featureFlagLiveData("new-dashboard").observe(this) { isEnabled ->
newDashboardView.visibility = if (isEnabled) View.VISIBLE else View.GONE
}
// Observe initialization state
viewModel.isInitialized.observe(this) { initialized ->
if (initialized) {
// Feature flags are ready
loadContent()
}
}
}
}
Feature Gates
Evaluate multiple feature flags together:
// All features must be enabled
viewModel.featureGateLiveData(
featureKeys = listOf("premium-tier", "beta-access"),
requirement = FeatureRequirement.ALL
).observe(this) { hasAccess ->
premiumSection.visibility = if (hasAccess) View.VISIBLE else View.GONE
}
// Any feature must be enabled
viewModel.featureGateLiveData(
featureKeys = listOf("experiment-a", "experiment-b"),
requirement = FeatureRequirement.ANY
).observe(this) { inExperiment ->
experimentBanner.visibility = if (inExperiment) View.VISIBLE else View.GONE
}
Negation
Invert the gate evaluation:
viewModel.featureGateLiveData(
featureKeys = listOf("maintenance-mode"),
requirement = FeatureRequirement.ALL,
negate = true // Show when maintenance-mode is OFF
).observe(this) { showMainContent ->
mainContent.visibility = if (showMainContent) View.VISIBLE else View.GONE
}
Identity Management
// Set user identity for targeting
viewModel.setIdentity("user-123")
// Clear identity
viewModel.setIdentity(null)
// Get current identity
val identity = viewModel.currentIdentity
Actions
// Refresh feature flags from server
viewModel.refresh()
// Clear cached feature flags
viewModel.clearCache()
// Get debug information
val debugInfo = viewModel.getDebugInfo()
View Extensions
Bind Views directly to feature flags using extension functions:
bindToFeatureFlag
Bind a View's visibility to a feature flag:
import io.toggly.views.bindToFeatureFlag
// Show when enabled, hide when disabled
newFeatureButton.bindToFeatureFlag(
featureKey = "new-feature",
lifecycleOwner = this
)
// Custom visibility states
betaSection.bindToFeatureFlag(
featureKey = "beta-access",
lifecycleOwner = this,
visibleWhenEnabled = View.VISIBLE,
visibleWhenDisabled = View.INVISIBLE // or View.GONE
)
bindEnabledToFeatureFlag
Bind a View's enabled state to a feature flag:
import io.toggly.views.bindEnabledToFeatureFlag
// Enable/disable based on feature flag
submitButton.bindEnabledToFeatureFlag(
featureKey = "submit-enabled",
lifecycleOwner = this
)
bindAlphaToFeatureFlag
Bind a View's alpha (opacity) to a feature flag:
import io.toggly.views.bindAlphaToFeatureFlag
// Fade out when disabled
experimentalSection.bindAlphaToFeatureFlag(
featureKey = "experimental",
lifecycleOwner = this,
enabledAlpha = 1.0f,
disabledAlpha = 0.3f
)
bindToFeatureGate
Bind visibility to multiple feature flags:
import io.toggly.views.bindToFeatureGate
adminPanel.bindToFeatureGate(
featureKeys = listOf("admin-access", "premium-tier"),
lifecycleOwner = this,
requirement = FeatureRequirement.ALL
)
showWhenFeature / bind with negate
Show a view when a feature is on, or use negate = true for the off path:
import io.toggly.views.showWhenFeature
import io.toggly.views.bindToFeatureFlag
// Show only when enabled
newFeatureView.showWhenFeature("new-feature", this)
// Show only when disabled
legacyFeatureView.bindToFeatureFlag(
featureKey = "new-feature",
lifecycleOwner = this,
negate = true
)
isFeatureOff remains available on the ViewModel as a helper for imperative checks.
toggleViews
Toggle between two views based on a feature flag:
import io.toggly.views.toggleViews
toggleViews(
featureKey = "new-checkout",
lifecycleOwner = this,
enabledView = newCheckoutView,
disabledView = legacyCheckoutView
)
Fragment Usage
Use the same patterns in Fragments:
class MyFragment : Fragment() {
private val viewModel: FeatureFlagViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Observe feature flags
viewModel.featureFlagLiveData("my-feature").observe(viewLifecycleOwner) { isEnabled ->
featureSection.visibility = if (isEnabled) View.VISIBLE else View.GONE
}
// Bind views
binding.newFeature.bindToFeatureFlag(
featureKey = "new-feature",
lifecycleOwner = viewLifecycleOwner
)
}
}
Flow-Based Usage
Access Flow-based APIs for more flexibility:
class MyFragment : Fragment() {
private val viewModel: FeatureFlagViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Collect feature flag flow
viewLifecycleOwner.lifecycleScope.launch {
viewModel.featureFlagFlow("my-feature").collect { isEnabled ->
updateUI(isEnabled)
}
}
// Collect feature gate flow
viewLifecycleOwner.lifecycleScope.launch {
viewModel.featureGateFlow(
listOf("feature-a", "feature-b"),
FeatureRequirement.ALL
).collect { hasAccess ->
updateAccessUI(hasAccess)
}
}
}
}
Suspend Functions
Use suspend functions for one-time checks:
viewLifecycleOwner.lifecycleScope.launch {
val isEnabled = viewModel.isFeatureOn("my-feature")
val isDisabled = viewModel.isFeatureOff("my-feature")
// Make decisions based on current values
if (isEnabled) {
showNewFeature()
}
}
RecyclerView Adapter
Use feature flags in RecyclerView adapters:
class MyAdapter(
private val items: List<Item>,
private val togglyService: TogglyService
) : RecyclerView.Adapter<MyViewHolder>() {
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = items[position]
// Bind feature flags
holder.itemView.showWhenFeature(
"show-item-details",
holder.itemView.findViewTreeLifecycleOwner()!!
)
}
}
Entity context
Pass the entity on each view check. Do not use setIdentity for page entities. See Entity & page context. Clients do not register schemas.
val on = Toggly.shared.isFeatureOn("OrderBadge", order, "Order")
Best Practices
1. Use ViewModel for Shared State
// Share ViewModel across Activity and Fragments
class MainActivity : AppCompatActivity() {
private val viewModel: FeatureFlagViewModel by viewModels()
}
class MyFragment : Fragment() {
private val viewModel: FeatureFlagViewModel by activityViewModels()
}
2. Cancel Jobs on Lifecycle End
View extension functions return Job for cancellation:
private val jobs = mutableListOf<Job>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
jobs += myView.bindToFeatureFlag("feature", viewLifecycleOwner)
}
override fun onDestroyView() {
super.onDestroyView()
jobs.forEach { it.cancel() }
jobs.clear()
}
3. Handle Configuration Changes
The ViewModel survives configuration changes:
viewModel.featureFlagsLiveData.observe(this) { flags ->
// This observer is re-attached after rotation
updateAllFeatures(flags)
}
Next Steps
- Learn about Jetpack Compose for modern UI
- Explore Storage Options for persistence
- Check Advanced Usage for more patterns