0Pricing
Flutter Mobile Development · Lección

Configuración remota, indicadores de funcionalidad y despliegues graduales

Active funcionalidades remotamente y distribuya los cambios gradualmente sin volver a enviar la aplicación.

Configuración remota, indicadores de funcionalidad y despliegues graduales es una lección gratuita de Flutter Mobile Development en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Flutter Mobile Development, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Flutter Mobile Development incluye 4 lecciones en total.

Partes de esta lección aún no han sido traducidas y se muestran en inglés.

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.

Preguntas frecuentes

¿La lección «Configuración remota, indicadores de funcionalidad y despliegues graduales» es gratis?

Sí — el texto completo de «Configuración remota, indicadores de funcionalidad y despliegues graduales» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Flutter Mobile Development, actualiza a CoddyKit PRO. El curso de Flutter Mobile Development incluye 4 lecciones en total.

¿Qué aprenderé en «Configuración remota, indicadores de funcionalidad y despliegues graduales»?

Active funcionalidades remotamente y distribuya los cambios gradualmente sin volver a enviar la aplicación. Practicas Flutter Mobile Development con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Flutter Mobile Development?

No se requiere experiencia previa. Flutter Mobile Development en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Configuración remota, indicadores de funcionalidad y despliegues graduales»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Flutter Mobile Development?

Sí. Cada lección de Flutter Mobile Development incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Variantes de compilación y configuración del entorno
  2. Canalizaciones automatizadas con Fastlane y GitHub Actions
  3. Informes de fallos y trazas de pila simbolizadas
  4. Configuración remota, indicadores de funcionalidad y despliegues graduales
← Volver a Flutter Mobile Development