Flutter Mobile Development · Lektion

Remote Config, Feature Flags und schrittweise Rollouts

Schalten Sie Features remote um und führen Sie Änderungen schrittweise aus, ohne die App erneut einzureichen.

Lektion 4 von 413 Schritte

Remote Config, Feature Flags und schrittweise Rollouts ist eine kostenlose Flutter Mobile Development-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Flutter Mobile Development-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Flutter Mobile Development-Kurs umfasst insgesamt 4 Lektionen.

Teile dieser Lektion wurden noch nicht übersetzt und werden auf Englisch angezeigt.

Why Remote Config

App store review cycles are slow. If a feature ships broken, you may wait days for a hotfix to reach users. Remote configuration decouples shipping the code from turning it on.

  • Feature flags gate code paths behind a boolean you control from a server.
  • Remote config delivers tunable values (strings, numbers, JSON) without a new build.
  • Staged rollouts expose a change to a growing percentage of users.

In Flutter the common tool is firebase_remote_config, but the same patterns apply to LaunchDarkly, ConfigCat, or your own backend.

Modeling a Flag Locally

Before wiring any SDK, model your flags as plain Dart so the rest of the app never touches raw strings. A typed config object keeps defaults explicit and makes the code testable.

This is pure Dart with sensible defaults baked in — exactly what you ship if the network is unavailable.

class AppConfig {
  final bool newCheckoutEnabled;
  final int maxUploadMb;
  final String welcomeMessage;

  const AppConfig({
    this.newCheckoutEnabled = false,
    this.maxUploadMb = 10,
    this.welcomeMessage = 'Welcome!',
  });
}

void main() {
  const defaults = AppConfig();
  print('checkout: ${defaults.newCheckoutEnabled}');
  print('maxUploadMb: ${defaults.maxUploadMb}');
  print('welcome: ${defaults.welcomeMessage}');
}

Firebase Remote Config Setup

With Firebase initialized, you grab the FirebaseRemoteConfig singleton, set in-app defaults, and configure fetch behavior via RemoteConfigSettings.

  • fetchTimeout caps how long a fetch waits.
  • minimumFetchInterval throttles fetches to respect quotas. Use a small value in debug, hours in production.

Defaults guarantee the app behaves sanely before the first successful fetch.

import 'package:firebase_remote_config/firebase_remote_config.dart';

Future<FirebaseRemoteConfig> initRemoteConfig() async {
  final rc = FirebaseRemoteConfig.instance;

  await rc.setConfigSettings(RemoteConfigSettings(
    fetchTimeout: const Duration(seconds: 10),
    minimumFetchInterval: const Duration(hours: 6),
  ));

  await rc.setDefaults(const {
    'new_checkout_enabled': false,
    'max_upload_mb': 10,
    'welcome_message': 'Welcome!',
  });

  return rc;
}

Fetch and Activate

Remote Config separates fetch (download values into a local cache) from activate (make the fetched values live). The convenience method fetchAndActivate() does both and returns whether new values were activated.

Always wrap the call: a network failure should fall back to the last activated values or your in-app defaults — never crash the launch path.

Future<void> refreshConfig(FirebaseRemoteConfig rc) async {
  try {
    final updated = await rc.fetchAndActivate();
    if (updated) {
      // New values are now live; rebuild dependent UI.
      print('Remote config activated with new values');
    }
  } catch (e) {
    // Offline or quota hit: keep last-known-good values.
    print('Remote config fetch failed, using cached/defaults: $e');
  }
}

Reading Typed Values

Remote Config stores everything as strings under the hood but exposes typed getters: getBool, getInt, getDouble, getString. Map them back into your AppConfig so the rest of the app stays typed.

Centralize this mapping in one place. If a key name changes, you fix it once.

import 'package:firebase_remote_config/firebase_remote_config.dart';

AppConfig readConfig(FirebaseRemoteConfig rc) {
  return AppConfig(
    newCheckoutEnabled: rc.getBool('new_checkout_enabled'),
    maxUploadMb: rc.getInt('max_upload_mb'),
    welcomeMessage: rc.getString('welcome_message'),
  );
}

Gating UI Behind a Flag

A feature flag should guard a single decision point. Read it once near the top of the widget tree (or from a provider) and branch.

Keep the gate shallow: branch on the flag, not deep inside business logic, so removing the flag later is a clean deletion.

Widget buildCheckout(BuildContext context, AppConfig config) {
  if (config.newCheckoutEnabled) {
    return const NewCheckoutScreen();
  }
  return const LegacyCheckoutScreen();
}

Parsing JSON-Valued Config

For richer payloads, store a JSON string in a single key and decode it. This lets one flag carry a whole structured experiment — themes, thresholds, ordered lists — without adding dozens of keys.

Below is standalone Dart showing the decode-and-fallback pattern you would apply to rc.getString('promo_banner').

import 'dart:convert';

class PromoBanner {
  final String text;
  final int priority;
  PromoBanner(this.text, this.priority);
}

PromoBanner parsePromo(String raw) {
  try {
    final map = jsonDecode(raw) as Map<String, dynamic>;
    return PromoBanner(
      map['text'] as String? ?? '',
      map['priority'] as int? ?? 0,
    );
  } catch (_) {
    return PromoBanner('', 0); // malformed JSON -> safe default
  }
}

void main() {
  final ok = parsePromo('{"text":"Summer Sale","priority":5}');
  print('${ok.text} (${ok.priority})');
  final bad = parsePromo('not-json');
  print('fallback text empty: ${bad.text.isEmpty}');
}

Staged Rollout by Percentage

A staged rollout exposes a feature to, say, 1% of users, then 10%, then 50%, then 100% — watching crash and metric dashboards at each step. Firebase Remote Config does this server-side with percentage conditions based on a stable, randomized user bucket.

The key requirement: bucketing must be stable. A user assigned to the rollout must stay in it across sessions, otherwise the UI flickers and metrics are meaningless.

Stable Client-Side Bucketing

If you roll your own backend instead of Firebase, you implement bucketing on the client. Hash a stable user id into a value in [0, 100) and compare against the rollout threshold. The same id always lands in the same bucket.

This deterministic hashing is the heart of staged rollouts and A/B splits.

int bucketOf(String userId) {
  // Simple stable hash -> 0..99
  var hash = 0;
  for (final code in userId.codeUnits) {
    hash = (hash * 31 + code) & 0x7fffffff;
  }
  return hash % 100;
}

bool isInRollout(String userId, int percent) {
  return bucketOf(userId) < percent;
}

void main() {
  const id = 'user-42';
  print('bucket: ${bucketOf(id)}');
  print('in 10% rollout: ${isInRollout(id, 10)}');
  print('stable on retry: ${bucketOf(id) == bucketOf(id)}');
}

Kill Switches and Safe Defaults

The most valuable flag is a kill switch: a remote boolean that instantly disables a risky feature without a release. Two rules make kill switches reliable:

  • The safe state must be the default. A kill switch should default to false (feature off) so a failed fetch never accidentally enables the risky path.
  • Read the flag at the decision point every time, not once at startup, so flipping it takes effect on the next config refresh.

For Remote Config, schedule a refresh on app resume so a kill switch propagates within one foreground cycle.

// Default OFF => a fetch failure leaves the risky feature disabled.
bool experimentalSyncEnabled(AppConfig config) {
  return config.newCheckoutEnabled; // example risky path
}

void onAppResumed(FirebaseRemoteConfig rc) {
  // Re-fetch so a flipped kill switch reaches the user quickly.
  refreshConfig(rc);
}

Listening for Real-Time Updates

Newer Firebase Remote Config supports real-time updates: a stream that fires when the server publishes new values, so you do not have to wait for the next scheduled fetch. You still call activate() to make them live.

Use this for kill switches where minutes matter. Combine the listener with setState/provider invalidation to rebuild affected widgets.

import 'package:firebase_remote_config/firebase_remote_config.dart';

void subscribeToUpdates(FirebaseRemoteConfig rc, void Function() onChanged) {
  rc.onConfigUpdated.listen((event) async {
    await rc.activate();
    onChanged(); // e.g. trigger a UI rebuild
  });
}

Quick Check

Test your understanding of safe rollout design.

Recap

You learned how to ship features dark and control them remotely:

  • Model flags as a typed AppConfig with explicit defaults.
  • Initialize Remote Config with setDefaults and tuned RemoteConfigSettings, then fetchAndActivate() defensively.
  • Read typed values and gate UI at a single shallow decision point; carry rich payloads as JSON strings.
  • Run staged rollouts with stable percentage bucketing so users stay consistently in or out.
  • Make every kill switch default to its safe state, and use real-time updates plus on-resume refresh so flips propagate fast.

Together these let you decouple deploy from release and respond to incidents without an app-store round trip.

Kostenlos starten

Lerne Dart mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
22
Lektionen
88

Häufig gestellte Fragen

Ist die Lektion „Remote Config, Feature Flags und schrittweise Rollouts“ kostenlos?

Ja — der vollständige Text von „Remote Config, Feature Flags und schrittweise Rollouts“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Flutter Mobile Development-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Flutter Mobile Development-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Remote Config, Feature Flags und schrittweise Rollouts“?

Schalten Sie Features remote um und führen Sie Änderungen schrittweise aus, ohne die App erneut einzureichen. Du übst Flutter Mobile Development mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Flutter Mobile Development zu starten?

Keine Vorkenntnisse erforderlich. Flutter Mobile Development auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Remote Config, Feature Flags und schrittweise Rollouts“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Flutter Mobile Development-Lektion Code schreiben und ausführen?

Ja. Jede Flutter Mobile Development-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Build-Flavors und Umgebungskonfiguration
  2. Automatisierte Pipelines mit Fastlane und GitHub Actions
  3. Crash-Reporting und symbolisierte Stacktraces
  4. Remote Config, Feature Flags und schrittweise Rollouts
← Zurück zu Flutter Mobile Development