In a real-world Flutter project, you usually develop against separate environments, such as development and production. Each environment can have its own back end, API, configuration, and even separate visual identities if you want to clearly differentiate them.
Flutter Flavors make it possible to run different versions of your app on the same device. At the same time, they keep those environments isolated from one another.
In this tutorial, you’ll transform a single-environment Flutter project called BuzzwordBingo into a multi-flavor app. In particular, you’ll learn how to:
- Isolate your configuration for each environment.
- Flavorize your Android and iOS apps.
- Flavorize the web part of your project.
- Integrate Firebase and work with separate Firebase projects.
So get ready to track all the buzzwords in the next marketing presentation you watch!
NB: In this tutorial, the Firebase configuration focuses on a typical use case with Firebase and Cloud Firestore. If your project requires native plugins like Firebase Analytics, Crashlytics or Performance Monitoring, you will need to configure Firebase integration differently. We might cover this more advanced use case in a future tutorial.
Getting Started
Download the starter project by clicking here. You can either clone this starter
branch with git or download a ZIP file by clicking the green Code button on Github. Open the project in the latest version of Android Studio with the latest version of the Flutter and Dart plugins installed.
Also, make sure that you use Flutter version 2.10 or above. Open pubspec.yaml and click Pub get in the upper right corner of the editor to download all dependencies.
Next, select a mobile simulator and run the app. You’ll see a home screen with three pre-filled buzzwords and a text field to add new ones.
BuzzwordBingo helps you keep track of all the buzzwords used in a marketing presentation. If you type a new buzzword in the top text field, and then the enter or the done button, you add a new buzzword to the list. If you type any of the existing buzzwords, you increase its count by one.
In the end, you’ll store buzzwords in a Firebase Firestore collection. But the starter project stores buzzwords in memory.
So if you restart the app, the state is reset. That’s not what you want, so you’ll need to add some persistence to this app. But first, you have to prepare your project to handle several environments. So, let’s start with configuration.
Isolating Your App’s Configuration
When your app is available in several flavors, you need to customize some settings for each of those flavors. For example, those settings can include the app’s name, colors and some of its keys for third-party APIs.
A naive approach would be to detect the app variant currently running everywhere you need to customize something. But that would quickly become unmanageable.
Instead, you’ll group all those environment-specific settings into the following AppConfig
class. In lib, create a new file named app_config.dart and add:
import 'package:flutter/material.dart';
// 1
enum Environment { dev, prod }
// 2
class AppConfig extends InheritedWidget {
// 3
final Environment environment;
final String appTitle;
// 4
const AppConfig({
Key? key,
required Widget child,
required this.environment,
required this.appTitle,
}) : super(
key: key,
child: child,
);
// 5
static AppConfig of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<AppConfig>()!;
}
// 6
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
}
Code language: Dart (dart)
Here’s a code breakdown:
- First, you declare an
Environment
enumerated type to define your environments. - Next, you declare the
AppConfig
class, which is anInheritedWidget
. You can attach anInheritedWidget
to aBuildContext
. This way, you can access thisInheritedWidget
from any of its descendants in the widget tree. - Your
AppConfig
class has two properties: one forenvironment
and one for the environment-specificappTitle
. - The constructor takes required values for your environment and environment-specific properties. It also takes a child widget.
AppConfig
will sit at the root of your widget tree. - The static
of
function makes it easy to access the closest instance ofAppConfig
by searching theBuildContext
parameter and all its parents. That’s whatBuildContext
‘sdependOnInheritedWidgetOfExactType
does. - Finally, since
AppConfig
extendsInheritedWidget
, it has to overrideupdateShouldNotify
. The framework calls this function to decide whether it should notify widgets that useAppConfig
when values insideAppConfig
change. Since allAppConfig
‘s properties arefinal
andAppConfig
is immutable, you can returnfalse
.
By default, when you build and run a Flutter project, it runs the main
function inside main.dart. To have several flavors of your app, you need one entry point for each flavor.
Inside lib, create a file named main_dev.dart and add the following code to it:
import 'package:flutter/material.dart';
import 'app_config.dart';
import 'buzzword_bingo_app.dart';
void main() {
// 1
const configuredApp = AppConfig(
child: BuzzwordBingoApp(),
// 2
environment: Environment.dev,
// 3
appTitle: '[DEV] BuzzwordBingo',
);
// 4
runApp(configuredApp);
}
Code language: Dart (dart)
The main
function:
- Wraps
AppConfig
around callingBuzzwordBingoApp
. - Sets the
environment
property to the dev environment. - Adds [DEV] to the
appTitle
. - Then it runs the app.
Next, in the lib folder, create a file called main_prod.dart.
import 'package:flutter/material.dart';
import 'app_config.dart';
import 'buzzword_bingo_app.dart';
void main() {
const configuredApp = AppConfig(
child: BuzzwordBingoApp(),
// 1
environment: Environment.prod,
// 2
appTitle: 'BuzzwordBingo',
);
runApp(configuredApp);
}
Code language: Dart (dart)
The code is almost the same as in main_dev.dart, except:
- The
environment
property is set for prod. appTitle
is the production title.
Now that you’ve set up the dev and prod environments, delete lib/main.dart.
Open lib/buzzword_bingo_screen.dart which declares the app’s main screen. After // TODO: replace with AppConfig-extracted app title
, replace the title
property with:
title: Text(AppConfig.of(context).appTitle),
Code language: Dart (dart)
Instead of using a generic constant, you inject the environment-specific value of appTitle
. There’s a compile error so make sure to import app_config.dart to resolve it.
import 'app_config.dart';
Code language: Dart (dart)
Next, open lib/buzzword_bingo_app.dart and look for the comment that says // TODO: replace with AppConfig-extracted app title
. Replace the title of your MaterialApp
, like you did before:
title: AppConfig.of(context).appTitle,
Code language: Dart (dart)
Like before, import app_config.dart as well.
import 'app_config.dart';
Code language: Dart (dart)
Last but not least, in Android Studio’s toolbar, click the build configuration dropdown. Then click Edit configurations….
Rename the Run/Debug configuration from main.dart to dev. In Dart entrypoint, replace main.dart at the end of the path with main_dev.dart.
Duplicate this configuration by selecting dev under Flutter in the left menu. Then click Copy Configuration.
Rename the new configuration prod.
In Dart entrypoint change main_dev.dart to main_prod.dart and click OK.
Back in the editor, select dev as your Run/Debug configuration and click Debug to run the app again.
Note that the title in the app bar contains [DEV].
Select prod as your build configuration. This time, the title is Buzzword Bingo:
If you run the app in a browser by selecting Chrome (web) as a Flutter Device, this is what you get for the dev flavor:
As you can see, both the title in the app bar and the page’s title in the browser tab are environment-specific.
Now you can install either the dev or the prod version of your app, but not both on the same mobile device yet. That’s because, on Android as in iOS, both versions of your app have the same identifier. You need to configure each native project to use a different identifier for each flavor.
You’ll start with the Android app.
Preparing Your Android App
In Android Studio, open android/app/build.gradle. In the defaultConfig
section, notice that applicationId
is com.epseelon.buzzwordbingo
.
That’s perfect for the production version of your app, but you want to override it for its development variant. This is where you’re going to use Android flavors.
Under // TODO: insert flavor configuration here
, add:
// 1
flavorDimensions "env"
// 2
productFlavors {
// 3
dev {
dimension "env"
applicationIdSuffix ".dev"
resValue "string", "app_name", "[DEV] BuzzwordBingo"
}
// 4
prod {
dimension "env"
resValue "string", "app_name", "BuzzwordBingo"
}
}
Code language: Gradle (gradle)
In this code:
- You declare a new flavor dimension called
env
. - Then you specify all the product flavors and override certain properties for each value of the
env
dimension. - For the
dev
environment, you add a suffix to theapplicationId
. This way, the identifier for thedev
version of your app iscom.epseelon.buzzwordbingo.dev
. You also define a new string resource calledapp_name
which you’ll use in a minute. - For the
prod
flavor, notice thatapplicationSuffix
isn’t listed. This is because you’re keeping the defaultapplicationId
without any suffix, but you set theapp_name
string resource to a different value.
Now, open android/app/src/main/AndroidManifest.xml. Find this comment: <!-- TODO change the android:label attribute here -->
.
Replace the content of the android:label
attribute with:
android:label="@string/app_name"
Code language: XL (xl)
This ensures that each flavor of your app shows a different name on the Android launcher.
Notice that android:icon
uses a mipmap resource called ic_launcher
. Thanks to the flavor configuration you added earlier, Gradle looks for this resource under android/app/src/dev and android/app/src/prod respectively for each flavor. You’ll find environment-specific icons in those folders.
Now that you customized the app’s identifier, name and icon, you need to update the dev and prod configurations. Click Edit Configurations again.
For the dev configuration, type dev
in the Build flavor field to point Flutter’s build to the dev flavor of the Gradle project as you configured it.
Do the same for the prod configuration, setting Build flavor to prod
.
Click OK.
Make sure you have an Android emulator running. First, run your app on the Android emulator with the dev configuration.
Now run the prod configuration.
Leave both apps to come back to the launcher. You’ll see that both flavors of the app are there, with different names and icons:
You can even run both of them at the same time, with different buzzwords.
Android app: check! Now on to your iOS app.
Preparing Your iOS App
Creating Build Configurations
In Android Studio, right-click the root of the project in the Project view, navigate to Flutter and choose Open iOS module in Xcode.
In Xcode’s Project navigator, select the root Runner, then Runner project and finally Info tab. In the outlined Configurations section, Xcode created three build configurations for you: Debug, Release and Profile.
First, rename each of these three configurations respectively to Debug-prod, Release-prod and Profile-prod.
Next, click the + button under the list of configurations. Then click Duplicate “Debug-prod” Configuration.
Rename the new configuration Debug-dev.
Do the same for Release-dev and Profile-dev until you get this list of configurations:
Next, you need to create separate schemes for dev and prod.
Creating Schemes
In Xcode’s top bar, click the Runner scheme, which has a green icon, and then Manage schemes….
In the dialog that pops up, rename the Runner scheme to prod. With this scheme still selected, click the ellipsis icon under the list of schemes and then Duplicate.
This creates a new scheme and opens a new dialog to configure it. Rename it to dev. Next, press Enter.
You’ll land back in the list of schemes:
Time to configure the build configurations each scheme uses. Double-click the dev scheme and the list of configurations is displayed.
Select Run and set the Build Configuration to Debug-dev. Repeat these steps for the Test and Analyze build configurations.
Now, select Profile and set its Build Configuration to Profile-dev.
Finally for Archive, set the Build Configuration to Release-dev.
Click Manage Schemes… to return to the list of schemes.
Double-click the prod scheme.
Configure all the build phases to use the -prod build configurations.
Next, you’ll override settings for each scheme and build configuration.
Customizing Settings for Each Scheme
In the Project navigator, select Info or Info.plist in the Runner group. Look for the Bundle display name entry and change BuzzwordBingo
to $(APP_NAME)
.
Now in the Project navigator, select Runner at the root again and then the Runner TARGET. Go to the Build Settings tab.
In the tab’s toolbar, click the + button and Add User-Defined Setting. Rename the new setting APP_NAME
to match the variable name you used earlier in Info.plist. Next, type [DEV] BuzzwordBingo
as a value in dev-related build configurations and BuzzwordBingo
for prod-related ones.
Next, in the search bar of the Build Settings tab, type AppIcon
.
Expand Primary App Icon Set Name. Then add the appropriate suffix corresponding to each build configuration:
These names match the assets included in the starter project.
Still in the Build Settings tab in the Runner target settings. This time, search for bundle identifier
. Expand the Product Bundle Identifier setting and add .dev
as a suffix for all three dev-related build configurations, like so:
Now, you can run both flavors of your app on the same iOS device.
Go back to Android Studio. Select an iOS simulator and the dev build configuration that points to the dev flavor.
Build and run. You’ll see the development app.
Run the prod build configuration and notice the title in the app bar is the prod version.
After running the prod version, look at the home screen and you’ll see both apps installed side by side on your iOS device, with different names and different icons.
Preparing Your Web App
In the starter project, you’ll find a webenv folder. This folder contains a subfolder for each environment. Inside each environment-specific folder, there’s a favicon.png and a couple of other icon images.
You’ll need to copy the content of the correct flavor-specific webenv subfolder to the web folder before you run your app. The starter project contains scripts to automate this process for both Windows and Unix/macOS.
The copy_webenv.* scripts are pretty straightforward.
Windows: xcopy webenv\%1 web\ /E/Y
Unix/macOS: cp -rf ./webenv/$1/* web/
These scripts take the environment name as a parameter; %1
and $1
respectively. So all you need to do is call that script with the right parameter right before running the app.
In Android Studio, click the build configuration drop-down menu and then Edit Configurations….
In the toolbar of the left menu, click the + icon to add a new configuration and scroll down and select Shell script.
Rename the shell script configuration to Copy webenv dev
.
In the Script path field, select either copy_webenv.sh if you’re on macOS or Linux, or copy_webenv.cmd in you’re on Windows. As shown earlier those scrips are at the project root level.
Then set Script options to dev
and leave Working directory to the root of your project.
Here is what you must end up with:
Using the Copy Configuration button in the toolbar of the left panel, duplicate the Copy webenv dev and rename the copy to Copy webenv prod. Type prod
in the Script options field.
Click Apply to save the new configurations.
Then, in the left panel, expand the Flutter section and select the dev configuration. In the Before launch section, click the + button, then Run Another Configuration:
Select Copy webenv dev in the list that pops up. Here is what you must end up with:
Repeat the process for the prod Flutter configuration. Add a new configuration before launch and select Copy webenv prod this time. Click OK.
Now select Chrome (web) as a target, the variant of your app you want to run and click Run or Debug.
Keep an eye on the favicon in the browser tab for each flavor:
Integrating Firebase
If you look at lib/buzzword_bingo_screen.dart, you’ll see that for now, the app reads buzzwords from a StreamBuilder
. It initializes this StreamBuilder
with a List
of Buzzword
s. It manipulates the Stream
using a StreamController
. So every time you rerun the app, it starts with the same initial list and forgets about the buzzwords you might have added or changed. It’s time to make this more persistent, but in a way that lets you manipulate separate databases for each flavor. You’ll do that using Firebase’s Cloud Firestore.
Note: Firebase integration into a Flutter project used to be more painful than it is now. You had to integrate it at a native level, download separate configurations for Android, iOS and the web and it was quite error-prone. But recently, the FlutterFire team changed that for the better. So if you already experienced the legacy Firebase/Flutter integration process, this tutorial will show you the new way. But again, keep in mind that certain Firebase features like Analytics, Crashlytics or Performance Monitoring still required legacy native configuration. We might cover this in a future tutorial.
Integrating Firebase Core
flutter pub add firebase_core
Code language: Shell Session (shell)
In Android Studio, open the Terminal panel and type the following command:
This will add the following line to pubspec.yaml:
firebase_core: ^1.15.0
Code language: YAML (yaml)
Note that the exact version may vary if a more recent version is available.
Next, since the new integration process uses FlutterFire CLI, which depends on Firebase CLI, you need to install Firebase CLI on a global level, and log in if you have not already done so. If you have NodeJS 10+ installed on your system, the easiest way to do it is to run the following command, whether you’re on Windows, Linux or macOS:
npm install -g firebase-tools
Code language: Shell Session (shell)
If you don’t have NodeJS, there are other ways described in Firebase CLI’s documentation.
Then, log in to your Firebase account with the following command:
firebase login
Code language: Shell Session (shell)
If you’re not logged in yet, this command will open a browser to let you log in to your account. Then, run the following command in the same terminal to install FlutterFire CLI:
dart pub global activate flutterfire_cli
Code language: Shell Session (shell)
Next, you need to prepare directories for your multi-flavor setup. In your project, create the following directories:
- lib/firebase/dev
- lib/firebase/prod
Then, back in the Terminal, run the following command to create a Firebase project for your dev app:
flutterfire configure -i com.epseelon.buzzwordbingo.dev \
-a com.epseelon.buzzwordbingo.dev \
-o lib/firebase/dev/firebase_options.dart \
--no-apply-gradle-plugins \
--no-app-id-json
Code language: Shell Session (shell)
This command will create a Firebase project and register an iOS app with com.epseelon.buzzwordbingo.dev
as a bundle identifier. It will also register an Android app with com.epseelon.buzzwordbingo.dev
as its package name. It will generate a configuration class in lib/firebase/dev/firebase_options.dart.
Finally, it will not apply gradle plugins in the Android project and it will not generate an application id file for iOS, which are only useful if you use Firebase services like Analytics, Performance Monitoring or Crashlytics
But the command is interactive. First, it retrieves a list of existing projects associated with the Firebase account you are logged in to. Using the arrow keys on your keyboard, place the cursor in front of <create a new project> and type Enter.
Then it asks for a project id for your new Firebase project, which needs to be unique across all of Firebase. So feel free to come up with a unique identifier of your own, something like buzzwordbingo-dev-<a number of your choice>
.
The next question is about which platforms you want your configuration to support. Leave the default selection of android, ios and web as it is.
Once the command is complete, it generates lib/firebase/dev/firebase_options.dart with the DefaultFirebaseOptions
class in it.
Next, repeat the process for the prod flavor of your app, using the following command:
flutterfire configure -i com.epseelon.buzzwordbingo \
-a com.epseelon.buzzwordbingo \
-o lib/firebase/prod/firebase_options.dart \
--no-apply-gradle-plugins \
--no-app-id-json
Code language: Shell Session (shell)
Notice that the iOS bundle identifier, the Android package name and the output are different.
Answer the questions like before and in the end, you’ll have a corresponding lib/firebase/prod/firebase_options.dart file.
NB: You should keep these firebase_options.dart files out of any public version control as they contain Google API keys you might want to keep private.
You can open the Firebase Console to see the two projects you have just created.
Back in Android Studio, open lib/main_dev.dart. Make the main()
function async
so that you can use await
inside it.
void main() async {
...
}
Code language: Dart (dart)
Then insert the following code at the beginning of the main()
function, right before the AppConfig
initialization:
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
Code language: Dart (dart)
The first line makes sure all the Flutter environment initialization is complete. Then you initialize Firebase itself using the DefaultFirebaseOptions
class that FlutterFire CLI generated earlier.
Add the following imports:
import 'package:firebase_core/firebase_core.dart';
import 'firebase/dev/firebase_options.dart';
Code language: Dart (dart)
Notice that since you’re in the dev entry point, you import the dev Firebase project configuration.
Repeat the same process for lib/main_prod.dart until you end up with:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase/prod/firebase_options.dart';
import 'app_config.dart';
import 'buzzword_bingo_app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
const configuredApp = AppConfig(
child: BuzzwordBingoApp(),
environment: Environment.prod,
appTitle: 'BuzzwordBingo',
);
runApp(configuredApp);
}
Code language: Dart (dart)
Enabling Firestore
Open Firebase Console and select the dev project you created earlier with FlutterFire CLI.
In the left menu, under the Build section, select Firestore Database.
Next, click the Create database button to enable Firestore. In the dialog that pops up, select Start in test mode.
Then, select the Cloud Firestore location closest to you and click Enable.
Repeat the same process in your prod Firebase project.
Then, back in Android Studio, in the Terminal, run the following command to add the latest version of Firestore as a dependency:
flutter pub add cloud_firestore
Code language: Shell Session (shell)
This will add the following line to your pubspec.yaml. The version might be different but that’s OK.
cloud_firestore: ^3.1.13
Code language: YAML (yaml)
Integrating Firestore in iOS
Then, open ios/Podfile, uncomment the line after the comment that says # TODO: Uncomment this line to define a global platform for your project
. Set the minimum iOS version supported by your app to 10.0
, which Cloud Firestore now requires:
platform :ios, '10.0'
Code language: plaintext (plaintext)
Still in ios/Podfile, replace # TODO: Add precompiled Firestore dependency here
with:
pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '8.15.0'
Code language: JavaScript (javascript)
This adds a precompiled version of some Firestore dependencies to greatly speed up your iOS build.
Then run the app on an iPhone simulator if you can. If your build fails with an error message that starts with Error running pod install
, look for another part of the error log that says something like this:
[!] CocoaPods could not find compatible versions for pod "FirebaseFirestore":
In Podfile:
FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `8.14.0`)
cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) was resolved to 3.1.13, which depends on
Firebase/Firestore (= 8.15.0) was resolved to 8.15.0, which depends on
FirebaseFirestore (~> 8.15.0)
Code language: Shell Session (shell)
It means that your version of cloud_firestore depends on a more recent version of the precompiled firestore-ios-sdk-frameworks dependency. Normally, the error message should also mention which version it expects. In that case, upgrade the dependency to that version in ios/Podfile, delete ios/Podfile.lock (if it already exists) and the entire ios/Pods directory, and run your app again.
Now, you need to configure Android for Firestore.
Integrating Firestore in Android
Open android/app/build.gradle and replace this line:
minSdkVersion flutter.minSdkVersion
Code language: Gradle (gradle)
With this one:
minSdkVersion 19
Code language: Gradle (gradle)
The Firestore SDK for Android also comes with a lot of functions. By default, Android limits the number of functions you can compile into an app. You need to enable multidex support to work around that. Flutter recently added a --multidex
command-line argument that should take care of that. But in my experience, it doesn’t work very well, so it’s best to do it manually. Still in android/app/build.gradle, replace the comment that says // TODO: enable multidex here
with:
multiDexEnabled true
Code language: Gradle (gradle)
Finally, at the end of the file, in the dependencies section, replace the comment that says // TODO: multidex dependency here
with:
implementation 'com.android.support:multidex:1.0.3'
Code language: Gradle (gradle)
With that, you can run the app on an Android simulator.
Updating BuzzwordBingo to Use Firestore
Open lib/buzzword_bingo_screen.dart. Replace the comment that says // TODO: Add Firestore properties here
with:
final _firestore = FirebaseFirestore.instance;
late CollectionReference<Map<String, dynamic>> _buzzwordsCollection;
Code language: Dart (dart)
Then, add the missing import:
import 'package:cloud_firestore/cloud_firestore.dart';
Code language: Dart (dart)
Next, replace the entire implementation of _watchBuzzwords()
with:
Stream<List<Buzzword>> _watchBuzzwords() {
// 1
_buzzwordsCollection = _firestore.collection('buzzwords');
// 2
return _buzzwordsCollection.orderBy('word').snapshots().map((snapshot) {
// 3
return snapshot.docs.map((document) {
// 4
final documentData = document.data();
return Buzzword(
word: documentData['word'] as String,
count: documentData['count'] as int,
);
}).toList();
});
}
Code language: Dart (dart)
- Create a reference to a
buzzwords
collection in your Firestore database. - Return a stream that receives an event each time a change occurs on your collection of buzzwords sorted by word.
- Each stream event is a Firestore
QuerySnapshot
that you need to map to a list ofBuzzword
s. - In turn, you can map each
QuerySnapshot
document onto a new instance ofBuzzword
.
With that, your grid of buzzwords will synchronize with your Firestore database.
Then, replace the implementation of _addWord(String word) {}
with:
void _addWord(String word) async {
// 1
_wordController.clear();
// 2
final buzzwords =
await _buzzwordsCollection.where('word', isEqualTo: word,).get();
if (buzzwords.size == 0) {
// 3
await _buzzwordsCollection.add(<String, dynamic>{
'word': word,
'count': 1,
});
} else {
// 4
final buzzwordDocument = buzzwords.docs.first;
final oldCount = buzzwordDocument.data()['count'] as int;
await buzzwordDocument.reference.update({'count': oldCount + 1});
}
}
Code language: Dart (dart)
- First, you clear the text field.
- You query the
buzzwords
collection to look for any existing buzzword with the same word. - If there are none, you create a new one with a count of 1.
- If there is one already, you update the existing one.
Finally, you can remove the declarations of the _buzzwords
and _buzzwordsStreamController
properties.
Running the App
Now you can run the dev app on a simulator:
It starts with an empty list of buzzwords since your database is still empty. Try adding a couple of buzzwords and incrementing their count:
Check your Firebase console and you’ll see the new buzzwords in the database:
Now run the prod version of your app. It starts with an empty screen, since it connects to an separate database. Try adding different buzzwords:
You can find the buzzwords you add in the Firebase console of your prod project too.
Last but not least, run the dev version of your app in a browser:
It starts with the buzzwords you created earlier, which demonstrates you have persistence now.
Where to go from here?
In this tutorial, I showed you how to make your Flutter project multi-flavored, how to isolate your configuration, run several flavors of the same app on the same device, and how to integrate this setup with Firebase as a back end.
You can download the final version of this project by cloning this final branch from Github, or downloading the ZIP file for it.
I have been wanting to create this tutorial for a long time, as a lot of those I found out there do not cover web configuration and/or the latest and greatest of FlutterFire.
In the future, I might follow up with more details about how to configure your multi-flavor project when you need Firebase libraries like Analytics, Crashlytics or Performance monitoring.
But for now, this should be enough to get you started with your next project. Of course, if you don’t use Firebase as your backend, it’s easy to add other environment-specific backend URLs to AppConfig
.
Hopefully, you will find this tutorial useful. If you have any question, feel free to leave them in the comments.