Practical guide: Flutter + Firebase + Codemagic

In the latest Stack Overflow developer survey, Firebase was among the most loved technologies in the database area. In the case you are new to Firebase, it’s a cloud-based service provided by Google to enhance mobile app development. Firebase takes the load of managing the in-house mobile application infrastructure. Firebase provides many useful services that make our lives easier, the most used services are:

  • Remote database
  • Remote config
  • Analytics
  • Authentication
  • Crash reporting (Crashlytics)

Most mobile app developers use Firebase services for a different purpose. Firebase also provides the test lab which offers test devices in the cloud for automated testing. Firebase supports both iOS and Android apps written using native technologies, such as Swift or Kotlin. On top of that, it also supports cross-platform mobile app development frameworks, such as Flutter. Firebase has official documentation on how to configure the Firebase SDK with Flutter apps. In this post, we will provide a step-by-step guide on how to set up the Firebase SDK for Flutter apps and publish those apps securely using the Codemagic CI/CD solution.

Prerequisites

In order to follow this guide, you need the following:

In this post, we will use the Codemagic-Demo app and set up Firebase plugins for the iOS version of the Flutter app. You can do a similar setup for the Android version of the Flutter app.

Firebase console: Project setup

To get started with Firebase, you need to have a Google account. Visit https://firebase.google.com/ and log in with your Google account credentials. You will be logged in to the Firebase console where you need to set up your project. Once logged in, you can create a project where you can add your iOS, Android or web apps. Detailed information about the Firebase project is available here, but we need to add the project name, project ID and accept the terms & conditions. Now, let’s create a Codemagic Demo project.

Firebase automatically assigns the project ID when the project name is entered, you can change the project ID at this stage. After this stage is complete, you cannot change the project ID anymore.

Once the project is created, you can access the dashboard of the project. Firebase has automatically provisioned all the resources required for the project.

Firebase: App setup

It’s time to add our iOS app to the Firebase project. Flutter creates both the iOS and the Android version of the app. In the Flutter app source code, you can see separate directories for the ios and android app. In order to add the app to Firebase, you have to complete the platform-specific setup. There is a separate set of instructions for Flutter projects which require fewer steps than the native apps. Let’s add the iOS version of the Flutter app to Firebase by clicking on the iOS icon in the project overview page. You will be asked to enter a few details of the iOS app, such as bundle ID, nickname and App Store ID.

Only the bundle ID is mandatory. You can get the bundle ID of the iOS app from Xcode. Open ios/Runner.xcworkspace in Xcode and click on Runner in the top left corner. You can then see the bundle identifier on the General tab of your main target.

In our case, it’s com.codemagic.demo. If you prefer the command line, you can execute the following command by passing the scheme and project workspace of your app.

$ cd ios $ xcodebuild -workspace Runner.xcworkspace/ -scheme Runner -showBuildSettings | grep PRODUCT_BUNDLE_IDENTIFIER

This will print the bundle identifier of your app. You can enter this identifier while registering the app with Firebase.

In the next step, Firebase will ask you to download the GoogleService-Info.plist file and put it in the iOS app directory. You can also download this anytime later.

In the case of iOS app setup, Firebase will guide you to create a Podfile and install pods, etc. However, you don’t have to perform these steps for the Flutter iOS app setup. Just proceed to the next steps until you see this screen.

At this stage, if you navigate the Firebase dashboard of your project, you will see that our iOS version of the Flutter app has been added to Firebase.

Similarly, you can also set up the Flutter Android app by following the instructions for Android, but we won’t go into this in this tutorial.

Get FlutterFire plugins

As mentioned earlier, Firebase provides different services, like analytics, remote config crash reporting, etc. For each service, there are specific Flutter plugins which are collectively known as FlutterFire. The plugins include platform-specific code to access services and APIs on each platform. As Flutter is a cross-platform mobile application development framework, these plugins can be used for both iOS and Android. You can find the list of all the available plugins on the FlutterFire documentation page.

In order to add Firebase services, we need to get the required plugins and add them to your pubspec.yaml file. Let’s get the Firebase Core Plugin and add it to the Codemagic-Demo project.

dependencies:
    flutter:
        sdk: flutter
    firebase_core: ^0.2.5

Once you add the dependency, you can download those dependencies using the following command:

$ flutter package get

This command will download the FlutterFire plugins and create the .flutter-plugins file in the root of the project. This file contains a list of the plugins. Firebase documentation shows how to add the Google Analytics FirePlugin, you can follow the documentation and configure the analytics plugin. However, we will configure a different plugin in this post.

Add the Crashlytics FlutterFire plugin

Let’s add the Crashlytics FlutterFire plugin to our Flutter project. This will report crashes in the Firebase dashboard. In order to add this plugin, you can follow the installation guide here. You need to add the firebase_crashlytics dependency in the pubspec.yaml file and run the following command:

$ flutter package get

This command will download the Crashlytics FlutterFire plugin. Once the plugin is downloaded, you can import it in the Flutter app and enable crash reporting for your app as described here.

In order to set up the Codemagic-Demo app for Crashlytics, we need to first set up CocoaPods and enable Crashlytics from Xcode as per the iOS instructions in the plugin documentation here. You have to add another script in the Xcode build phase, as shown below.

If you face any CocoaPods related issues, you may need to run the pod install command from the ios directory. This will install all the necessary iOS libraries for the Flutter plugins.

This step is needed to run the app locally and get the Firebase Crashlytics service working. After that, you can see a sample code in the main.dart file of the plugin example app.

The example app has a working demo of using Firebase and the Crashlytics Flutter plugin. Once the code is embedded in our app and you run the app, we will see that our app is reporting the crash data and other logs to Firebase. The example app has buttons to trigger crashes, throw an error, etc. Flutter logs also show the events as they occur.

Once you trigger the events, you can see those crashes reported in the Firebase console as well.

Now, we have successfully added Firebase for our Flutter app. As the next step, we will securely configure using Firebase on Codemagic CI/CD.

Configure Firebase on Codemagic (encryption)

Codemagic is the official CI/CD solution for Flutter apps. You can set up your Flutter project for continuous integration and continuous delivery within a minute and without any configuration. If you are new to Codemagic, you can read the getting started guide. At this point, we have successfully configured our app with Firebase and Firebase Crashlytics Flutter plugin. We will now have to configure the same on CI. Before proceeding, we have to consider the security aspects of our Firebase account.

In our local configuration, we have downloaded the GoogleService-Info.plist file and kept it in the ios directory of the Flutter app. This file contains sensitive data, like the API key and information about the app. This file shouldn’t be checked in to source control or exposed to the world. We have to take certain steps to secure our sensitive data.

Ignore GoogleService-Info.plist in SCM

First, we have to ignore the GoogleService-Info.plist file to avoid it being checked in to the source control manager, such as Git. You can add this file to your .gitignore file depending on where you keep the sensitive file. For example:

ios/GoogleService-Info.plist
ios/Runner/GoogleService-Info.plist

Once the file added to the .gitignore list, it cannot be committed to source control by mistake.

Encrypt GoogleService-Info.plist with a strong key

At this stage, we have ensured that we won’t commit the GoogleService-Info.plist file to source control. However, the continuous integration server will expect to find this file in order to successfully build the Flutter app. There are several ways to encrypt sensitive files with a strong encryption password and decrypt them on the CI server using the same password. On the local macOS machine, you can execute the following command to encrypt the file.

$ openssl enc -aes-256-cbc -salt -in ios/Runner/GoogleService-Info.plist -out ios/Runner/GoogleService-Info.enc -k "YOUR_SECRET_PASSWORD"

This will create an encrypted version of the file, ios/Runner/GoogleService-Info.enc, which can be safely checked in to source control. However, your encryption password should be super strong. This encryption password will be used on Codemagic to decrypt the file.

Decrypt GoogleService-Info.enc on Codemagic using a secret environment variable

At this stage, we have ios/Runner/GoogleService-Info.enc checked in to the repository. Before building, we have to decrypt the file using the encryption password used earlier. Codemagic provides a way to securely store sensitive information using environmental variables. In the app settings, you can expand the Environment variables section and add a variable FIREBASE_KEY with the value of your encryption password. Don’t forget to check Secure to ensure that the value won’t be displayed in the build logs.

Now that we have created the variable FIREBASE_KEY, we can use it in the custom script before the build.

In the Codemagic app setting, before the build stage, click on the + button to add the following pre-build script.

#!/bin/bash
openssl enc -aes-256-cbc -d -in ios/Runner/GoogleService-Info.enc -out ios/Runner/GoogleService-Info.plist -k "$FIREBASE_KEY"

openssl enc -aes-256-cbc -d -in ios/Runner/GoogleService-Info.enc -out ios/GoogleService-Info.plist -k "$FIREBASE_KEY"

echo "Decrypted GoogleService-Info.plist Successfully using your strong encryption key set in the Codemagic ENV variable section "

echo "Listing iOS Directory to confirm the GoogleService-Info.plist is there! "
ls ios/Runner/

In the Codemagic UI, this script will look like this:

Click the Save button so that the script will be saved for the pre-build phase. You don’t need to worry about removing the files as the builder VMs are wiped clean after every build.

In the pre-build phase, we will see that the file is available for the build. The build logs look like this:

Now that we have securely added the sensitive file to Codemagic as a secure environment variable, we can see that the Codemagic build is successfully passed.

We have successfully built the Flutter app with Firebase on Codemagic using encryption.

Firebase on Codemagic (encoding)

There is another technique that can be used for securing the content of the sensitive files on the CI server. There is already a blog post on how to use the encoding technique on Codemagic to secure the Firebase sensitive files. In this approach, we are using an encoding technique rather than an encryption technique. It’s a very similar approach but we have to encode the file locally and decode while executing on Codemagic. The GoogleService-Info.plist file can be encoded locally using the following command:

$ openssl base64 -in ios/GoogleService-Info.plist

This command will print the encoded content which we need to paste in the Codemagic environment variable.

In the Codemagic app settings, expand the Environment variables section and add the variable FIR_ENCODE which can be used to decode the file back to the original.

In the case of iOS, we have to run the custom script in the pre-build phase. Add the following pre-build script in Codemagic.

#!/bin/sh
echo "==Decoding GoogleService-Info.plist File"
echo $IOS_FIREBASE | /usr/local/Cellar/base64/1.5/bin/base64 --decode > $FCI_BUILD_DIR/ios/GoogleService-Info.plist
echo "==Successfully Decoded GoogleService-Info.plist File"

In Codemagic, it looks like this:

After this script, we will get the original GoogleService-Info.plist file and carry on with our build execution. You can read the detailed steps of the encoding technique in this blog post.

We have now seen how to integrate Firebase into Flutter apps and run the apps securely on the Codemagic CI/CD service. When you’re using encryption, the sensitive file is still stored in the repository, although in encrypted form. With environment variables, you can securely store the file on Codemagic instead of the repository. You can choose whichever technique is more convenient for your project setup.

Source code

The source code of this tutorial is available in the firebase branch of Codemagic-Demo repository. Feel free to use it for trying out your setup.

$ git clone [https://github.com/Shashikant86/Codemagic-Demo](https://firebase.google.com/ "https://firebase.google.com/")
$ cd Codemagic-Demo
$ git checkout firebase

In case you encounter any issues related to CocoaPods, you need to set up CocoaPods using the pod init command and probably need to run pod install locally.

Conclusion

Firebase provides out-of-the-box support for Flutter apps and there are lots of FlutterFire plugins available. With the combination of Flutter, Firebase and Codemagic, you can deploy the Flutter apps securely with Firebase features enabled in the production.

Codemagic CI for Flutter