Open In App

Remote Config in Flutter and Firebase

Last Updated : 11 Mar, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In Current app development, it is essential to offer smooth updates without asking users to download a new version from the Play Store to improve user experience. Remote Config in Flutter with the power of Firebase allows developers to update app content and features dynamically without releasing a new release.

We have all felt this in popular applications like WhatsApp, Instagram, and YouTube ,etc. Where new features or changes to the UI are seen without any manual upgrade. This is implemented by using Firebase Remote Config, through which developers can change app behavior and look immediately. Also ,using Remote Config we can:

  • Roll out new features incrementally.
  • Personalize UI components for various user segments.
  • Test versions of the app A/B.
  • Resolve issues in real-time .

By including Remote Config within your Flutter application, you're able to remain flexible, drive engagement, and deliver a personal experience—all without asking users to reinstall or manually update the application.

Steps to implement Firebase remote config in Flutter Application

Step 1 : Create a new flutter application

Refer this article to know how to create a new flutter application : Creating a Simple Application in Flutter.

Step 2 : Integrate firebase into flutter application

Refer this article to know how to integrate firebase into flutter application : How to Add Firebase to Flutter App?

Step 3 : Add the dependency to pubspec.yaml file

- For Implementing Firebase remote config in your project first you have to add the dependency in your pubspec.yaml file.

dependencies:
firebase_remote_config: ^5.4.2

- Now run the below command in terminal to update the pubspec.yaml.

flutter pub get


Step 4 : Return Material App in main.dart() file

First, create MyApp() in StatelessWidget and in it return MaterialApp(). Now in MaterialApp() give the title of the app and add debugShowCheckModeBanner as false which will remove the debug banner in the app.

Now represent first screen home: Homepage().

main.dart:

Dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:oyo/home_page.dart';
import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}


Step 5 : Design a UI Component

Create a new file called buttonAndText.dart and design a UI Component Class for row of Button and Text.

buttonAndText.dart:

Dart
// A stateful widget that combines
// a button and a text display
import 'package:flutter/material.dart';

class ButtonAndText extends StatefulWidget {
  
  const ButtonAndText({
      Key? key,
      required this.defaultText,
      required this.onPressed,
      required this.buttonText,
  }) : super(key: key);

  // The default text to display
  final String defaultText;
  
  // The text to display on the button
  final String buttonText;
  
  // The function to call when the
  // button is pressed
  final Future<String> Function() onPressed;

  @override
  State<ButtonAndText> createState() => _ButtonAndTextState();
}

class _ButtonAndTextState extends State<ButtonAndText> {
  
  // The text to display, initially null
  String? _text;

  @override
  void didUpdateWidget(covariant ButtonAndText oldWidget) {
      super.didUpdateWidget(oldWidget);
      
      // Update the text if the default text changes
      if (widget.defaultText != oldWidget.defaultText) {
          setState(() {
            _text = widget.defaultText;
          });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8.0),
      child: Row(
        children: [
          
          // Display the text
          Expanded(
            child: Text(
                _text ?? widget.defaultText,
                style: const TextStyle(fontSize: 16),
            ),
          ),
          const SizedBox(width: 16),
          
          // Display the button
          ElevatedButton(
            style: ElevatedButton.styleFrom(
                foregroundColor: Colors.white,
                backgroundColor: Colors.green,
            ),
            onPressed: () async {
                
                // Call the onPressed function and
                // update the text with the result
                final result = await widget.onPressed();
                setState(() {
                  _text = result;
                });
            },
            child: Text(widget.buttonText),
          ),
        ],
      ),
    );
  }
}

here didUpdateWidget is Called whenever the widget configuration changes.


Step 6 : Create parameters in Firebase

For that, you need to follow below steps.

  1. Go to firebase console.
  2. Select your project.
  3. Open side navbar and select Remote Config which is inside the "Run".
  4. Click on Create Configuration in that screen.
  5. It will ask you to create parameter ,so enter key (type of the key is only string) and value (type of the value might be String, Boolean, JSON, number based on our requirement) in that screen and click on save.
  6. It will ask you to Publish Changes ,so click on that button.
  7. After that it will again for conformation, so click on it again.
  8. Congratulation parameters successfully created!

If you any confusion in above steps watch below video and do it.


Step 7 : Start Coding in HomePage.dart

We have few sub-functionalities to implement firebase remote config below:

1. Initialization:

Dart
// Get the instance of FirebaseRemoteConfig
final FirebaseRemoteConfig remoteConfig =FirebaseRemoteConfig.instance;

// Set default values for
// Remote Config parameters
await remoteConfig.setDefaults(<String, dynamic>{
        
        // Default value for 'welcome' key
        'welcome': 'welcome', 
        
        // Default value for 'new_update' key
        'new_update': false   
});

// Set configuration settings
// for Remote Config
await remoteConfig.setConfigSettings(
    RemoteConfigSettings(
        
        // Timeout for fetching
        fetchTimeout: const Duration(seconds: 10), 
        
        // Minimum interval between fetches
        minimumFetchInterval: const Duration(minutes: 5), 
    ),
);

Explanation:

  • Take instance of the remote config:
final FirebaseRemoteConfig remoteConfig  =  FirebaseRemoteConfig.instance;
  • Set default values of remote config parameter using setDefaults() method, it will only take Map<String, dynamic>.
await remoteConfig.setDefaults(<String, dynamic>{
// Default value for 'welcome' key
'welcome': 'welcome',

// Default value for 'new_update' key
'new_update': false
});
  • Set configuration settings for Remote Config using setConfigSettings() method, it will take RemoteConfigSettings() constructor with two parameters:
    • fetchTimeout: Maximum Duration to wait for a response when fetching configuration from the Remote Config server.
    • minimumFetchInterval: Maximum age of a cached config before it is considered stale.
await remoteConfig.setConfigSettings(
RemoteConfigSettings(

// Timeout for fetching
fetchTimeout: const Duration(seconds: 10),

// Minimum interval between fetches
minimumFetchInterval: const Duration(minutes: 5),
),
);


2. Fetch parameter:

Dart
// Get the instance of FirebaseRemoteConfig
final FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.instance;

// Set configuration settings
// for Remote Config
await remoteConfig.setConfigSettings(
    RemoteConfigSettings(
        
        // Timeout for fetching
        fetchTimeout: const Duration(seconds: 10),
        
        // Minimum interval between fetches
        minimumFetchInterval: Duration.zero, 
        ),
    );

// Fetch and activate the
// remote config values
await remoteConfig.fetchAndActivate();

print("Fetched: ${remoteConfig.getString('welcome')}");

Explanation:

  • fetchAndActivate() : Performs a fetch and activate operation, as a convenience.
  • remoteConfig.getString('welcome') : it will give you the value of welcome parameter in firebase remote config.


3. Listen to remote config updates:

Dart
// Remote Config update stream
remoteConfig.onConfigUpdated.listen((event) async {
    
        // Activate the fetched remote
        // config values
        await remoteConfig.activate();
        
        // Update the state with the new
        // config values
        setState(() {
                
                // Store the update event
                update = event; 
                
                // Check if there is a new update
                newUpdate = remoteConfig.getBool('new_update'); 
        });
});


Explanation:

  • remoteConfig.onConfigUpdated.listen((event)... : Starts listening for real-time config updates from the Remote Config backend and automatically fetches updates from the RC backend when they are available.
  • remoteConfig.activate(): Makes the last fetched config available to getters.

Complete Code in HomePage.dart

Dart
import 'dart:async';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:app/buttonAndText.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  StreamSubscription? subscription;
  RemoteConfigUpdate? update;
  bool newUpdate = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Remote Config Example'),
        backgroundColor: Colors.green,
        foregroundColor: Colors.white,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            
            // ButtonAndText widget to
            // initialize Remote Config
            ButtonAndText(
              
              // Default text to display
              // before initialization
              defaultText: 'Not initialized',
              
              // Text on the button
              buttonText: 'Initialize',
              
              // Function to execute when
              // the button is pressed
              onPressed: () async {
                
                // Get the instance of
                // FirebaseRemoteConfig
                final FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.instance;

                // Set default values for
                // Remote Config parameters
                await remoteConfig.setDefaults(<String, dynamic>{
                      
                      // Default value for 'welcome' key
                      'welcome': 'welcome', 
                      
                      // Default value for 'new_update' key
                      'new_update': false 
                });

                // Set configuration settings
                // for Remote Config
                await remoteConfig.setConfigSettings(
                      RemoteConfigSettings(
                            
                            // Timeout for fetching
                            fetchTimeout: const Duration(seconds: 10),
                            
                            // Minimum interval between fetches
                            minimumFetchInterval: const Duration(minutes: 5), 
                      ),
                );

                // Return a message indicating
                // initialization is complete
                return 'Initialized';
              },
            ),
            const SizedBox(height: 16),
            
            // ButtonAndText widget to fetch
            // and activate Remote Config
            ButtonAndText(
              defaultText: 'No data',
              buttonText: 'Fetch Activate',
              onPressed: () async {
                try {
                  
                  // Get the instance of
                  // FirebaseRemoteConfig
                  final FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.instance;

                  // Set configuration settings
                  // for Remote Config
                  await remoteConfig.setConfigSettings(
                    RemoteConfigSettings(
                      
                      // Timeout for fetching
                      fetchTimeout: const Duration(seconds: 10),
                      
                      // Minimum interval between fetches
                      minimumFetchInterval: Duration.zero, 
                    ),
                  );

                  // Fetch and activate the
                  // remote config values
                  await remoteConfig.fetchAndActivate();
                  
                  print("Fetched: ${remoteConfig.getString('welcome')}");
                  return 'Fetched: ${remoteConfig.getString('welcome')}';
                } on PlatformException catch (exception) {
                  print(exception);
                  return 'Exception: $exception';
                } catch (exception) {
                  print(exception);
                  return 'Unable to fetch';
                }
              },
            ),
            const SizedBox(height: 16),
            
            // ButtonAndText widget to listen
            // for Remote Config updates
            ButtonAndText(
              defaultText: update != null ? 'Updated keys: ${update?.updatedKeys}' : 'No data',
              buttonText: subscription != null ? 'Cancel' : 'Listen',
              onPressed: () async {
                try {
                  final FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.instance;
                  if (subscription != null) {
                    await subscription!.cancel();
                    setState(() {
                      subscription = null;
                    });
                    return 'Listening cancelled';
                  }

                  // Update the state to start listening
                  // for Remote Config updates
                  setState(() {
                      
                    // Subscribe to the Remote
                    // Config update stream
                    subscription = remoteConfig.onConfigUpdated.listen((event) async {
                      
                      // Activate the fetched
                      // remote config values
                      await remoteConfig.activate();
                      
                      // Update the state with
                      // the new config values
                      setState(() {
                        
                        // Store the update event
                        update = event; 
                        
                        // Check if there is a new update
                        newUpdate = remoteConfig.getBool('new_update'); 
                      });
                    });
                  });
                  return 'Listening, waiting for update...';
                } on PlatformException catch (exception) {
                  print(exception);
                  return 'Exception: $exception';
                } catch (exception) {
                  print(exception);
                  return 'Unable to listen';
                }
              },
            ),
            const SizedBox(height: 20),
            
            // Display a message if there is a new update
            if (newUpdate)
              Container(
                decoration: BoxDecoration(
                  color: Colors.green,
                  borderRadius: BorderRadius.circular(8),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.5),
                      blurRadius: 5,
                      spreadRadius: 2,
                    ),
                  ],
                ),
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text(
                    "This is a new dynamic update from firebase",
                    style: const TextStyle(color: Colors.white),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

For checking on the whole application code refer to Remote Config in Flutter and Firebase.

Output:


Next Article

Similar Reads