Feature Flags setup in Flutter with Firebase Remote Config

David Ordine
tarmac
Published in
8 min readSep 19, 2023

This article aims to teach how to setup some feature flags logic in your flutter app using Firebase Remote Config free service, leveraging of its amazing real time updates feature 🚀. For those who like to check the whole code, an example app was implemented in this GitHub repository: https://github.com/DavidGrunheidt/flutter-feature-flags-firebase-example.

Before starting coding, let's discuss a little bit about feature flags.

Why should I use Feature Flags on my Flutter app?

  • For apps not in production yet, feature flags are a good way to easily enable or disable an entire feature. Say you just created this app and are posting it in production the first time. You can easily disable a feature in case there’s an error that was discovered only in production. The optimal case of course is not having any errors in production, but we all know that may happen from time to time, and with feature flags you have one more tool in your hand to prevent the users from encountering an error. After you turn off that feature you can work on the fix.
  • For apps already in production, using feature flags and staged rollouts can help you out releasing a feature progressively for a percentage of users. You can start with a small amount like 1%, 5% or 10%, depending on how many users your app already has. After that, you can monitor your crash report service (Firebase Crashlytics for example) for unusual crashes, exceptions and errors. After some time, if everything seems normal, you can increase the feature flag rollout percentage and repeat the loop until the rollout is on 100%
  • With feature flags you can also have specific enabling conditions using a user property like city. This depends on your “user” model of course. For example, you can activate a feature only for the user with city = SĂŁo Paulo. Firebase has audiences, which groups users under some conditions including user properties. This allows you to have a feature flag condition by a desired audience.

Feature flags with Firebase Remote Config setup steps:

1 — Go to https://console.firebase.google.com/ and check if you already have a project for your current Flutter app. If not, click on Add Project and go through the steps. It will ask you to activate Analytics. If you want the ability to activate and deactivate feature flags by some user property, like email, city, age, or something like that, you should activate analytics. If you already have a project, check if analytics is enabled on the left menu under the Analytics Dashboard.

2 — On your pubspec.yaml add the firebase dependencies. You can check this file under the example GitHub repository. After that, open a terminal, navigate to the root folder of your project and run a flutter pub get.

  firebase_core: ^2.15.1
firebase_analytics: ^10.4.5
firebase_remote_config: ^4.2.5

3— Configure Firebase. Nowadays this is done automatically thanks to the Firebase CLI tool. You can check their official docs on how to do it https://firebase.google.com/docs/flutter/setup#install-cli-tools. If the docs aren't enough, here's how to do it in your terminal

  • 3.1 Run firebase login . This will open a new window on your default web browser. You should login with the google account that contains the Firebase project you want to add to your Flutter app.
  • 3.2 Run dart pub global activate flutterfire_cli . These commands enable flutterfire command, which we'll use in the next step.
  • 3.3 Run flutterfire configure . If everything went right before it will ask you to select which project you want to add to your Flutter app. You can scroll up or down using arrow keys and enter for selecting.
  • 3.4 After that it will ask which platforms your app supports. You can select or deselect each one with space and scroll with the arrows again. Once you are done, just hit enter again.
  • 3.5 Android and iOS apps will be registered on Firebase, files for each platform will be fetched and a configuration file will be created under lib/firebase_options.dart . If you already have that file, it will ask if you want to override it.

4 — Create a file called feature_flag_keys.dart that will contain all your feature flag keys. These keys are used to access a bool value stored on Firebase Remote Config.

// Feature flags
const kEnableMyCarTabKey = 'enable_my_car_tab';
const kEnableMyCarMileageKey = 'enable_my_car_mileage';
const kEnableMyCityTabKey = 'enable_my_city_tab';

5— Create a class called RemoteConfigRepository that will handle all your remote config logic. In the given example we are using MobX for defining observable variables that trigger state changes and widget rebuilds when their value changes. Variables defined on feature_flag_keys.dart will be accessed here. The implementation of this class will totally vary depending on how you are implementing state management in your project.

import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';

import '../helpers/feature_flag_keys.dart';

part 'remote_config_repository.g.dart';

class RemoteConfigRepository = _RemoteConfigRepository with _$RemoteConfigRepository;

abstract class _RemoteConfigRepository with Store {
final _remoteConfig = FirebaseRemoteConfig.instance;

@observable
bool enableMyCarTab = false;

@observable
bool enableMyCarMileage = false;

@observable
bool enableMyCityTab = false;

@action
Future<void> init() async {
try {
if (!kIsWeb) _remoteConfig.onConfigUpdated.listen(_updateConfigs, onError: (_) {});
await _setDefaultConfigs();

await _remoteConfig.setConfigSettings(
RemoteConfigSettings(
fetchTimeout: kDebugMode ? const Duration(minutes: 1) : const Duration(seconds: 10),
minimumFetchInterval: kDebugMode ? const Duration(hours: 4) : const Duration(minutes: 1),
),
);

await _remoteConfig.fetchAndActivate();
} finally {
await _updateConfigs(RemoteConfigUpdate({}));
}
}

@action
Future<void> _updateConfigs(RemoteConfigUpdate remoteConfigUpdate) async {
await _remoteConfig.activate();

enableMyCarTab = _remoteConfig.getBool(kEnableMyCarTabKey);
enableMyCityTab = _remoteConfig.getBool(kEnableMyCityTabKey);
enableMyCarMileage = _remoteConfig.getBool(kEnableMyCarMileageKey);
}

Future<void> _setDefaultConfigs() async {
return _remoteConfig.setDefaults({});
}
}
  • 5.1 On init method, _remoteConfig.onConfigUpdate streams updates from Firebase Remote Config. You can listen to these updates using listen method of Stream. When a new update arrives, _updateConfigs runs and your feature flags variables can be updated inside this function. If these variables are being observed in a Widget, any change on them will trigger a widget rebuild which may hide an existing feature or show a new feature. In our case the variables are enableMyCarTab , enableMyCityTab and enableCarMileage but this will totally change according to your needs.
  • 5.2 We also fetch values one time on init to update previously cached ones and call updateConfigs to update our observable variables the first time.

6— Create a class AnalyticsRepository that will handle your analytics logic, such as logging events and setting user properties.

import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/foundation.dart';

import '../helpers/app_constants.dart';
import '../models/user_info.dart';

class AnalyticsRepository {
static final analytics = FirebaseAnalytics.instance;

Future<void> init() {
setUserProperties(userInfo);
return analytics.setAnalyticsCollectionEnabled(kReleaseMode);
}

Future<void> setUserProperties(UserInfo userInfo) async {
if (kDebugMode) print('[AnalyticsRepository] - setUserProperties ${userInfo.toJson()}');

for (final userProperty in userInfo.toJson().entries) {
await analytics.setUserProperty(name: userProperty.key, value: userProperty.value.toString());
}
}
}
  • 6.1 setAnalyticsCollectionEnabled should be called after asking for App Tracking Transparencypermission on iOS and on Android can be set to true .
  • 6.2 setUserProperties can be called after the user performs a login operation on your app, for example, with the user data retrieved from your API. Just be sure to not call this function every time the app starts.

7 — Implement access logic to the classes create at items 5 and 6. In our case these classes will be located using GetIt . File dependency_locator.dart initializes all dependencies and also calls they init method, if they have one. setupRepositoryDependencies() will be called on main function.

import 'package:get_it/get_it.dart';

import '../../repositories/remote_config_repository.dart';
import '../repositories/analytics_repository.dart';

GetIt dependencyLocator = GetIt.instance;

Future<void> setupRepositoryDependencies() async {
dependencyLocator.registerLazySingleton<RemoteConfigRepository>(RemoteConfigRepository.new);
dependencyLocator.registerLazySingleton<AnalyticsRepository>(AnalyticsRepository.new);

await dependencyLocator<AnalyticsRepository>().init();
await dependencyLocator<RemoteConfigRepository>().init();
}

8 — Call WidgetsFlutterBinding.ensureInitialized(), Firebase.initializeApp() and setupRepositoryDependencies() in your main function. This way you make sure that everything is initialized before being used.

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();

await setupRepositoryDependencies();

return runApp(const MyApp());
}

Creating flags on Firebase Remote Config and testing:

Last steps are all it takes for setting up a feature flag approach to your Flutter app. These next steps will cover how to create feature flags on Firebase Remote Config as bool values and test their change in the app. We will use examples to demonstrate how to set and use these flags. The example app uses MVVM architecture with GetIt for dependency locator and MobX for state management. It has a feature called Home with three tabs:

  • Home: Initial view with a welcome message
  • My Car: Displays the user car model and its mileage. The tab is only shown if enable_my_car_tab remote config bool value is true. Also, the mileage is only displayed if enable_my_car_mileage remote config bool value is true. Both these values act as a feature flag.
  • My City: Displays the user city name. It’s only shown if enable_my_city remote config bool value is true.

You can check the example feature implementation here: https://github.com/DavidGrunheidt/flutter-feature-flags-firebase-example/tree/master/lib/features/home

9 — Go to https://console.firebase.google.com/ , select your project and on the left menu find Remote Config . You can create a new key:value pair clicking the button Add Parameter . You can also add parameters to groups if you think your app is going to have a lot of remote configs. Be sure to add the same key name you created on feature_flag_keys.dart. Here's a gif showing how to create a new parameter:

10 — Check if your UI element attached to some feature flag is changing when you change the flag on Firebase Remote Config. In the example case, My Car and My City tabs should be enabled/disabled when we change enable_my_car_tab and enable_my_city_tab . Inside My Car tab, car mileage should be displayed only if enable_my_car_mileage is true. Let's check if this is happening:

As you can see, the changes happened automatically without needing to rebuild the app, which means the user will get these types of changes automatically in their apps, while in use, without needing to reopen it. For this you need to implement feature flags variables in your code as observable variables that trigger widget rebuilds, and have the realtime listener configured to update these, as we did on item 5.

11 — Create Custom Definitions on Firebase according to your user properties. This example has a user model with carName , carMileage , city . If your user has a lot of fields, try to use the most important ones, which you know that can be used to differentiate/group them, or that it's already used by your company as a grouping field.

12 — Create conditions and use these custom definitions when needed, or create a condition for staged rollouts for feature flags. You can create a condition where a random user is in a percentage, and attach that to a feature flag to enable it only for 5% of your users, for example.

That's basically it for starting to use feature flags in your Flutter app. These examples are just the tip of the iceberg on what you can do with Firebase services for implementing Feature Flags. Hope you enjoy and feel free to leave a comment asking something. Check out my profile for other tutorials https://medium.com/@davidordine

--

--

David Ordine
tarmac
Writer for

Principal Flutter Engineer at tarmac.io, currently living in FlorianĂłpolis, Brazil. Loves to travel, surf and snowboard.