Multiple Builds Environment in Flutter for Firebase Using Provider and Flavor

Ken Lee Chiaw Huat
Flutter Community
Published in
7 min readApr 23, 2020

It is always a good practice to build separate apps from the same source code and for different environment such as development, staging and production. Why? When you build a system, most of the time, there will be additional bugs fixing and enhancements. Thus, in order not to break the production environment especially the database side of it, we need to separate the development and the testing environment.

Officially, the team behind Flutter recommended the approach of using flavors — a common practice for native Android development.

The word flavor means ‘version’ and basically, we create multiple flavors of our mobile app that we can build the app with different configuration options and styles such as different database environment (development, staging or production).

We will be using the previous Flutter with Firebase CRUD project template (here) that I created previously, and further enhance it to include flavors. If you would like to know more about the template that I created, click on the link here.

At the end of this article, we will have a working Flutter project that support 2 environments (development and production) using flavor. Each app will display the app name specifically to their flavor and connecting to different Firebase project.

Take note, this article is mainly for Flutter in Android environment and not the iOS.

Step 1: Create 2 Different Firebase Projects and Configuration

For this, as mentioned earlier, we will be defining 2 environments namely development and production, so we will need to create 2 Firebase projects.

Then, for each Firebase projects, create a Cloud Firestore database. Do remember to have at least 1 sign-in method enabled for both projects — for this example, we are using Email/Password only.

Next, we need to get the google-services.json for each Firebase project. To do that, lets assume we will be using the following applicationId for both environment:

  • - Note App Prod: applicationId: com.example.create_flutter_provider_app.prod
  • - Note App Dev: applicationId: com.example.create_flutter_provider_app.dev

You can use your preference naming convention for the applicationId, as long it is understandable that 1 is for production environment and another is for development environment.

With the applicationId defined, we will used it to add Firebase to our project. Go to each Firebase project, click on the small Android icon and the following screen will appear. Add the applicationId to the right Firebase project as specified above.

Once registering the app, download the google-services.json for each Firebase projects.

At the end of this step, you should have 2 Firebase projects with the following setup done:

  • Note App Prod

→ Sign-in method configured

→ Created Cloud Firestore Database

→ applicationId: com.example.create_flutter_provider_app.prod

→ google-services.json file downloaded

  • Note App Dev

→ Sign-in method configured

→ Created Cloud Firestore Database

→ applicationId: com.example.create_flutter_provider_app.dev

→ google-services.json file downloaded

Step 2: Updating Project Config Files

Add in the following configuration into the project android/app/build.gradle:

Here, we created 2 flavor. 1 is for development environment (dev) and another is for production (prod). The most important piece of information that you must remember is the applicationId field. Again, take note, the applicationId must match those specified in Step 1. As for the versionCode and versionName, this is just example. For me, I prefer to have some version name that I can refer later without touching the code layer.

Step 3: Create 2 folders Representing Development and Production

The folder name must match the named specified in productFlavors (android/app/build.gradle). The previous downloaded google-services.json files for both Firebase projects need to be moved to the right folder. For the file downloaded from Note App Prod, place it under the folder prod and for the same file name downloaded from Note App Dev, place it under the dev folder.

For the sub folder called ‘res’, ignore it for now. We will go through that shortly.

Step 4: Having different app name and app icon for Dev and Prod

We want to have a different app name for our project. This is helpful as with Flavor setup, we can install both app in the same devices for testing. Having app name such as NoteApp-Dev or Note-Dev or Note-Prod helps us to identify which app is for which environment — development or production.

The same concept and benefits if we have different app icon for each apps that are connecting to different environment.

For app name, create file called strings.xml and with the following content:

<?xml version=”1.0" encoding=”utf-8"?>
<resources>
<string name=”app_name”>Note-Dev</string>
</resources>

Change the app_name to fit your Dev app. Put this file under the dev/res/values:

Do the same for the prod environment but name your app_name to be Note-Prod or something that represent it is the app for Production.

<?xml version=”1.0" encoding=”utf-8"?>
<resources>
<string name=”app_name”>Note-Prod</string>
</resources>

The final step for the app name is to change the Android manifest file to refer to the String name app_name. To do that, open the file AndroidManifest.xml located at android/app/src/main folder. Update the android:label as follow:

android:label="@string/app_name"

Next, for the images, copied all 5 sub folders that start with the name mipmap-xxx from main/res/, and paste it to the res folder for both dev and prod. At the end, you should have the following view:

If you notice, each sub-folder with the name minimap contains images and that image is the app icon image. Change it to your needs.

Step 5: New Dart files and Code Updates

Create a flavor.dart file. This will just contain enum value that represent both dev and prod environment that the main class can refer and decide. The code:

enum Flavor {dev, prod}

Since we are using the Flutter template shared above, we will need to create additional main.dart that represent the production build and using the original main.dart to be representing as development build.

Our goal is to able to build and run different flavor as follow:

flutter run --flavor dev -t lib/main.dart
flutter run --flavor prod -t lib/main_prod.dart

First, update the main.dart as follow:

void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_) async {
runApp(
MultiProvider(
providers: [
Provider<Flavor>.value(value: Flavor.dev),
ChangeNotifierProvider<ThemeProvider>(
create: (context) => ThemeProvider(),
),
ChangeNotifierProvider<AuthProvider>(
create: (context) => AuthProvider(),
),
ChangeNotifierProvider<LanguageProvider>(
create: (context) => LanguageProvider(),
),
],
child: MyApp(
databaseBuilder: (_, uid) => FirestoreDatabase(uid: uid),
),
),
);
});
}

As mentioned before, we will make the main.dart as the representative of building a development build. As this project is using Provider, we will pre-set the main.dart to use the dev flavor.

Duplicate this file and renamed it as main_prod.dart. Update the Flavor.dev to Flavor.prod.

Then, apart from the previous app name and app icon changes, we felt it is also necessary to display some info at the first screen namely the sign-in screen. Thus, since we are using Provider to set the initial value of the both main.dart and main_prod.dart, we then can access this initial default value at the sign-in screen as follow:

Text(
Provider.of<Flavor>(context).toString(),
),

So, what will happen if we were to run the following code:

flutter run --flavor dev -t lib/main.dart

Flutter will try to build and run the app using the flavor dev and using the main.dart file. This will result the following display of sign-in screen.

Notice the bottom part, it contains the value of the enum value dev.

The command: flutter run — flavor dev -t lib/main.dart is basically telling Flutter to build the app for testing using the flavour dev specified in the build.gradle. Also, the second part is basically telling it to run it using main.dart file. And since our main.dart has the initial flavor value pre-set as dev, it will be display as Flavor.dev.

If were to run the command: flutter run — flavor prod -t lib/main_prod.dart, will result the following:

Notice the bottom of the screen, it is displaying Flavor.prod.

There you have it, a more complete and updated Flutter template that utilizing the benefits of flavors.

Link to github is here.

Again, I do hope the above explanation and additional improvement to the Flutter template will help even more in your project as much as it helps me.

Cheers.

--

--

Ken Lee Chiaw Huat
Flutter Community

Technical Blogger | Mobile Developer@Flutter | Software Consultant | Father | Entrepreneur