What You'll Learn
This article explains Firebase Remote Config (real-time updates, feature flags, A/B testing, Compose integration).
Setup
// build.gradle.kts
dependencies {
implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
implementation("com.google.firebase:firebase-config-ktx")
}
Setting Default Values
<!-- res/xml/remote_config_defaults.xml -->
<?xml version="1.0" encoding="utf-8"?>
<defaultsMap>
<entry>
<key>feature_new_ui</key>
<value>false</value>
</entry>
<entry>
<key>welcome_message</key>
<value>Welcome!</value>
</entry>
<entry>
<key>max_items</key>
<value>20</value>
</entry>
</defaultsMap>
Repository Implementation
class RemoteConfigRepository @Inject constructor() {
private val remoteConfig = Firebase.remoteConfig.apply {
setConfigSettingsAsync(remoteConfigSettings {
minimumFetchIntervalInSeconds = if (BuildConfig.DEBUG) 0 else 3600
})
setDefaultsAsync(R.xml.remote_config_defaults)
}
private val _configUpdates = MutableSharedFlow<Unit>(replay = 1)
init {
remoteConfig.addOnConfigUpdateListener(object : ConfigUpdateListener {
override fun onUpdate(configUpdate: ConfigUpdate) {
remoteConfig.activate().addOnCompleteListener {
_configUpdates.tryEmit(Unit)
}
}
override fun onError(error: FirebaseRemoteConfigException) {
Log.e("RemoteConfig", "Update error", error)
}
})
fetchAndActivate()
}
private fun fetchAndActivate() {
remoteConfig.fetchAndActivate()
}
fun getBoolean(key: String): Boolean = remoteConfig.getBoolean(key)
fun getString(key: String): String = remoteConfig.getString(key)
fun getLong(key: String): Long = remoteConfig.getLong(key)
val updates: SharedFlow<Unit> = _configUpdates
}
Feature Flags
@HiltViewModel
class MainViewModel @Inject constructor(
private val remoteConfig: RemoteConfigRepository
) : ViewModel() {
val isNewUiEnabled: StateFlow<Boolean> = remoteConfig.updates
.map { remoteConfig.getBoolean("feature_new_ui") }
.stateIn(viewModelScope, SharingStarted.Eagerly, remoteConfig.getBoolean("feature_new_ui"))
val welcomeMessage: StateFlow<String> = remoteConfig.updates
.map { remoteConfig.getString("welcome_message") }
.stateIn(viewModelScope, SharingStarted.Eagerly, remoteConfig.getString("welcome_message"))
}
Compose Screen
@Composable
fun HomeScreen(viewModel: MainViewModel = hiltViewModel()) {
val isNewUi by viewModel.isNewUiEnabled.collectAsStateWithLifecycle()
val welcomeMessage by viewModel.welcomeMessage.collectAsStateWithLifecycle()
Column(Modifier.padding(16.dp)) {
Text(welcomeMessage, style = MaterialTheme.typography.headlineMedium)
Spacer(Modifier.height(16.dp))
if (isNewUi) {
NewFeatureCard()
} else {
LegacyContent()
}
}
}
@Composable
fun NewFeatureCard() {
Card(Modifier.fillMaxWidth()) {
Column(Modifier.padding(16.dp)) {
Text("New Feature", style = MaterialTheme.typography.titleMedium)
Text("This new UI is enabled via Remote Config")
}
}
}
Summary
| Feature | Implementation |
|---|---|
| Initialization | Firebase.remoteConfig |
| Default values | setDefaultsAsync() |
| Real-time | addOnConfigUpdateListener |
| Feature flags | getBoolean("key") |
| A/B testing | Firebase Console configuration |
- Use
fetchAndActivate()to get latest values -
ConfigUpdateListenerreflects changes in real-time - Default values XML works offline
- Set up A/B testing in Firebase Console
8 production-ready Android app templates (Firebase integrated) are available.
Browse templates → Gumroad
Related articles:
- Firebase Auth
- Firebase Firestore
- Firebase Messaging
Ready-Made Android App Templates
8 production-ready Android app templates with Jetpack Compose, MVVM, Hilt, and Material 3.
Browse templates → Gumroad
Top comments (0)