Part 2 - Preparing a Release build for Android

Part 2 - Preparing a Release build for Android

In Part 1, you created a Play Console account, which you will soon use to deploy your app to the Play Store. However, the Play Console requires that you upload a release app bundle for distribution.

The official documentation is a thorough primary reference for how to create a release app bundle. However, it assumes some knowledge that newer app developers may lack and several steps are unnecessary for many apps. So, this guide is a simplified version of the official one, with a few extra tips, concept explanations, context and references.

In other words, this guide is most suitable for developers publishing their first app. However, if your app is large (>64K methods), if you use Platform Views, if you have multiple build flavours, or if you plan to publish someplace other than the Play Store, you should refer to the official documentation instead.

It's important to give credit where due, so the parts of this guide that are copied straight from the official documentation are marked with <From Docs></From Docs>

Step 1 - Make Launch Icons

To create your launch icon, the official documentation suggests using flutter_launcher_icons or creating them manually.

Many developers prefer a third process, described here.

Step 2 - Create a Keystore and Upload Key

If you already have a keystore and upload key, skip to Step 3.

Your app bundle should be signed with an "upload key" before it is uploaded to the Play Console. From there, Google can use "Play App Signing" (recommended) to sign the app before it is released to the Play Store. Read here for more information on the signing process.

To create your keystore and upload key, follow the instructions below from the docs.

<From Docs>

On Mac/Linux, use the following command:

keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload

On Windows, use the following command:

keytool -genkey -v -keystore %userprofile%\upload-keystore.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload

</From Docs>

After creating your keystore and upload key, note down the name and location of your newly created keystore. If you copied straight from the above, your file will be named upload-keystore.jks and in your home folder.

Finally, note down the passwords you just created for your keystore file and key (preferably in the password manager of your choice). This information is needed in the Step 3.

A final note on app signing:

If you find the concept of app signing confusing, you are not alone! Many developers -- even experienced ones -- have not encountered the concept until they try to publish an app for the first time. You can still publish your app by following this guide, without understanding the details.

However, if you are new to or confused by the concept, I strongly recommend taking the time to learn a little more about public key cryptography generally and Android app signing specifically. This deeper understanding is useful multiple times:

  • now, when signing your Android app for the Play Store

  • later, when signing your iOS app for the App Store (a different, more complicated process)

  • finally, when you configure your apps for CI/CD with AppCircle

Step 3 - Connect your Keystore and Key to the App

To reference the newly created keystore and key in your app project, create a file called key.properties at <project_root>/android/key.properties and add the following:

<From Docs>

storePassword=<password from previous step>
keyPassword=<password from previous step>
keyAlias=upload
storeFile=<location of the key store file, such as /Users/<user name>/upload-keystore.jks>

</From Docs>

This file includes the key and keystore's password in plain text! If you are using git, go double-check your .gitignore file and make sure it includes key.properties. If you are not using git, take appropriate steps to protect the key.properties file.

Step 4 - Configure Signing of your Release build in Gradle

In Flutter, there are 3 types of builds - Debug, Profile and Release. If you have been developing your Flutter app with the command flutter run or pressing a Play button (without any extra configuration in your IDE) until now, you have been creating Debug builds. To publish an app to the Play Store, you must create a Release build signed with the upload key we created in the previous steps.

The Debug and Release build types differ in a few important ways:

  • Release builds are optimized for smaller size and faster execution

  • It's not possible to debug or hot reload Release builds (this is why you develop with a Debug build)

  • Release builds can be signed, which you must do before uploading your app bundle to the Play Store

In Android, by default, there are Debug and Release type builds (although you can configure others, such as staging or beta). The build configurations for the different build types of an Android app are defined in the build.gradle file.

When you are finally ready to create Release build of your Flutter app for upload to the Play Console, the instructions for building and signing the app are defined in the build.gradle file found in the <project root>/android/app/build.gradle folder (not the one in the <project root>/android/build.gradle).

So, let's add the app signing instructions to your app build.gradle file now by following the instructions in the official docs.

<From Docs>

  1. Add the keystore information from your properties file before the android block:

        def keystoreProperties = new Properties()
        def keystorePropertiesFile = rootProject.file('key.properties')
        if (keystorePropertiesFile.exists()) {
            keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
        }
    
        android {
              ...
        }
    

Load the key.properties file into the keystoreProperties object.

  1. Find the buildTypes block:

       buildTypes {
            release {
                // TODO: Add your own signing config for the release build.
                // Signing with the debug keys for now,
                // so `flutter run --release` works.
                signingConfig signingConfigs.debug
            }
        }
    

    And replace it with the following signing configuration info:

        signingConfigs {
            release {
                keyAlias keystoreProperties['keyAlias']
                keyPassword keystoreProperties['keyPassword']
                storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
                storePassword keystoreProperties['storePassword']
            }
        }
        buildTypes {
            release {
                signingConfig signingConfigs.release
            }
        }
    

Release builds of your app will now be signed automatically.

Note: You may need to run flutter clean after changing the gradle file. This prevents cached builds from affecting the signing process.

</From Docs>

Step 5 - Update the Android Manifest and Gradle files

5a - Choosing an App Id

To upload an Android app to the Play Console, it must have a unique app id. If you created your Flutter project using Android Studio, you may already have an appropriate app id.

However, when you make a new Flutter project with flutter create or a different IDE, the default com.example.<app_name> is used. The com.example namespace is restricted and you cannot upload an app to the Play Console with it.

So, now it is time to update your app id to something else. The app id name follows Java/Kotlin package naming conventions, with a few added rules. Here are the app id naming rules to keep in mind:

  • it must have at least 2 segments (the segments are the strings separated by dots)

  • Valid characters for the segments: [a-zA-Z0-9_]

  • Each segment must start with a letter

If you have a website, it is common to use that namespace. For example, I use dev.aerial.<app_name>. Read more about Android app ids here.

IMPORTANT! After you have uploaded your app to the Play Console, you cannot change the app id. If you do, it will be treated as a new, separate app.

5b - Gradle Update

After deciding on your app id, you can return to same the build.gradle file you edited in Step 4 (<project root>/android/app/build.gradle). Look for the defaultConfig block (inside the android block) and modify the applicationId parameter with the name you have chosen.

5c - Android Manifest Update - App ID

Now open up the Android manifest file found at <project root>/android/app/src/main/AndroidManifest.xml If this is the first Flutter app you are publishing to the Play Store, you may not have encountered it before. For now, you should know that the Android Manifest is used to:

  • Declare system permissions (eg., access photos or access the internet)

  • Declare the app components. Your Flutter app will (likely) only need the one main Activity declared there by default.

It also has more advanced functionality which can read about here.

So, open up the Android Manifest file and search for the android:label field inside the <application> block. Edit the value there to reflect the name of your app. This string is displayed with your launch icon on Android devices.

5d - Android Manifest Update - Internet permission

Finally, if your app uses the internet, you need to add an internet permission to your main activity. It is included by default in the debug manifest (found at <project root>/android/app/src/debug/AndroidManifest.xml) used in debug builds. So, you may not have noticed it was missing if you were only using debug builds until now.

To add internet to your release build, add the following line inside the <manifest> block:

<uses-permission android:name="android.permission.INTERNET"/>

The top of your manifest file should now look something like this (with your own android:label and package string)

5e - Extra credit - Android package name

The next step is not strictly necessary, but I recommend it for completeness:

In the <manifest> block (top of the file), look for the package field. Update this to match the app id you defined earlier in the build.gradle file. The default value is com.example.<app name> If curious, you can read more about Android package name vs. Application id here.

Step 6 - Create a signed Release build

You are finally ready to create a release build! Open a terminal, cd into your project root and run the following command:

flutter build appbundle

This will create an .aab file (app bundle) in the following location:

<project root>/build/app/outputs/bundle/release/app.aab

app.aab is the file you will eventually upload to the Play Console for release to the Play Store. Read Part 3 (coming soon) to learn about how you use the Play Console to publish your app to the Play Store.