With Flutter's firebase_remote_config, how does a parameter move from setDefaults through fetch and activate to what getBool returns?
answer
- three layers: default, fetched, active
- fetch stores, activate applies
- fetchAndActivate returns a bool
- only bool, num and String defaults
- missing key: false, 0, empty string
basics
~20 ssetDefaults supplies in-app values; fetch() downloads backend values into a cache without applying them; activate() makes them what getBool and the other getters return. fetchAndActivate() does both and returns true only when new values were activated.
solid answer
~40 s`FirebaseRemoteConfig.instance` keeps three sets of values. `setDefaults` holds in-app defaults, which accept only `bool`, `num` or `String` (anything else throws `ArgumentError`); they apply before the first fetch and for keys the backend does not set. `fetch()` downloads the published config and caches it, but getters still return the **active** set. `activate()` promotes the fetched set, and `fetchAndActivate()` does both in one call. Both return `true` only if newly fetched values were activated, and `false` if they were already active, which is not an error. `getBool`, `getInt`, `getDouble` and `getString` read the active values and fall back to `false`, `0`, `0.0` or `''` for unknown keys. `getValue(key).source` tells you whether a value is `valueRemote`, `valueDefault` or `valueStatic`.
code
dart · 20 linesimport 'package:firebase_remote_config/firebase_remote_config.dart';
Future<bool> loadCheckoutFlag() async {
final rc = FirebaseRemoteConfig.instance;
await rc.setDefaults(const {
'new_checkout_enabled': false,
'checkout_banner_text': 'Free delivery over 50',
});
try {
final applied = await rc.fetchAndActivate();
print('new values activated: $applied'); // false = nothing new, not an error
} on FirebaseException catch (e) {
print('fetch failed (${e.code}); using active or default values');
}
final flag = rc.getValue('new_checkout_enabled');
print('source: ${flag.source}'); // valueRemote, valueDefault or valueStatic
return rc.getBool('new_checkout_enabled');
}go deeper
Recall the order: setDefaults, fetch, activate, or fetchAndActivate for both, then read with getBool, getInt, getDouble or getString.
Explain why fetched values stay invisible until activation, what the boolean from fetchAndActivate means, and what getters return for missing keys.
Show when you activate so screens do not change under users, how you debug a stuck flag with getValue().source, and how a failed fetch degrades to defaults.
Decide which behaviour belongs behind remote parameters at all, weighing defaults that must be safe offline, readable-by-anyone values, and the cost of flags that never get removed.
## The model: three layers of values **Remote Config** lets you change app behaviour from the Firebase console without shipping a new build. For example, you can decide whether users see the new checkout flow. In Flutter the client is the **firebase_remote_config** plugin, and `FirebaseRemoteConfig.instance` is its entry point. The instance holds three layers: | Layer | Filled by | Read by getters? | |---|---|---| | In-app defaults | `setDefaults(Map<String, dynamic>)` | yes, when no active remote value exists | | Fetched config | `fetch()` | **no**, not until activated | | Active config | `activate()` or `fetchAndActivate()` | yes | The split between **fetched** and **active** is the point of the design. Downloading new values never changes what the running app reads. Only an explicit activation does, so you choose the moment the UI changes. ## Defaults first Call `setDefaults` early, before anything reads a flag: - Values must be `bool`, `num` or `String`. Anything else, such as a `Map`, throws an `ArgumentError`, whose message tells you to convert JSON to a string first. - Defaults make the app behave sensibly on first launch, offline, or when the backend has no value for a key. - The upstream guide warns that end users can read every default and fetched value, so never put secrets in Remote Config. ## Fetch, then activate 1. `fetch()` asks the backend for the published template and caches it. It can throw a `FirebaseException` with codes such as `network-error`, `throttled` or `forbidden`. 2. `activate()` copies the last fetched config into the active layer. It returns `true` if parameters were activated, and `false` if they were already active. 3. `fetchAndActivate()` performs both and returns `activate()`'s result. A `false` result simply means nothing new was applied. A common pattern is to activate at startup, before the first screen that depends on a flag is built. Another is to fetch now and activate on the next launch, so a screen does not change under the user's finger. The guide suggests activating at a moment that keeps the experience smooth. ## Reading values - `getBool`, `getInt`, `getDouble` and `getString` read the **active** layer, or the default when there is no active remote value. - For a key that exists nowhere, they return `false`, `0`, `0.0` and `''`. A typo in a key name therefore silently reads as "off". - `getBool` treats the strings `true` and `1` as true, ignoring case. - `getValue(key)` returns a `RemoteConfigValue` whose `source` is `valueRemote`, `valueDefault` or `valueStatic`. This is useful when a flag is not behaving as expected. - `getAll()` returns every parameter as a `Map<String, RemoteConfigValue>`. - `ensureInitialized()` makes sure the last activated config is loaded, so getters return it. ## Worked example: a new-checkout flag - `setDefaults({'new_checkout_enabled': false})` keeps the old flow as the safe default. - At startup, `fetchAndActivate()` applies whatever the console currently publishes, subject to the fetch interval. - The checkout route reads `getBool('new_checkout_enabled')`. - If the flag seems stuck, `getValue('new_checkout_enabled').source` shows whether the app ever received a remote value. When the backend will allow the next fetch (`minimumFetchInterval`) and how live updates arrive (`onConfigUpdated`) are covered elsewhere. Rollout percentages and conditions are part of release practice, not of this API.
- Why does Remote Config separate fetch from activate instead of applying values as soon as they download?Applying values mid-screen can change layout or flow under the user. With the split, you can fetch in the background and activate at a safe moment, such as the next launch or before the checkout route is built. The active layer only changes when your code calls `activate()` or `fetchAndActivate()`.
- You need a structured config object, such as a list of coupon rules. How do you store it?`setDefaults` accepts only `bool`, `num` or `String`, so store JSON as a string parameter. Read it with `getString` and decode it with `jsonDecode` into your model, with a safe fallback if parsing fails. The `ArgumentError` for a `Map` default exists to push you into exactly this.
Think of a shop. The in-app defaults are the price tags printed on the packaging. fetch() is a delivery of new price labels that goes into the back room. Nothing on the shelf changes until someone walks out and sticks them on, which is activate(). Customers (the getters) only ever read what is on the shelf.
saying these in an interview costs you the question
- fetch() makes the new values visible to getBool immediately.
- fetchAndActivate returning false means the fetch failed.
- setDefaults accepts nested maps for structured config.
- getBool throws when the key does not exist.
- Remote Config is a safe place for API keys because it is fetched over TLS.