Photo by Rami Al-zayat

How to build and sign your Flutter Android application using GitHub Actions

Save time with continuous integration

Posted by Damien Aicheh on 04/29/2021 · 7 mins

With GitHub you have the ability to quickly set up your continious integration using GitHub Actions to build your application for your project regularly and distribute it to your users.

Steps overview

In order to achieve your goal you need to follow these steps:

  • Set up Java to compile your Android project
  • Specify the correct version of Flutter
  • Restore the pub packages
  • Build the application to generate your Android App Bundle
  • Sign it using the right keystore
  • Share it to your users

To be able to complete this tutorial, you need to have access to your keystore file with the associated passwords.

With that ready let’s get started!

Introducing GitHub Actions

The idea is to automate the process of generated packages using GitHub Actions, this process is called continuous integration.

When using GitHub Actions you have:

  • An access to it from everywhere
  • You will not be in charge of server maintenance
  • You have it all in one place: GitHub

We will create something called a workflow that will allow you to generate your Android App Bundle with just a single click.

Setup your own GitHub Actions

Let’s create our first workflow! Inside your project, you need to create a workflows folder inside the .github folder and then create a new file called: android-release.yml for instance. This file will contain our first job called build_android:

name: Flutter_Android

    branches: [main]

   runs-on: ubuntu-latest

    - name: Checkout the code
      uses: actions/checkout@v2

This job will be triggered when you will push new changes on the main branch. The first step that we will use is to check out the code of our branch.

Setup the environment

To build our Flutter Android application, we need to use a default action called: actions/setup-java@v1 that will add all the necessary tooling for Java.

- name: Setup Java to compile Android project
  uses: actions/setup-java@v1
    java-version: '12.x'

Next, to use Flutter in our workflow we need to install it, thanks to an action from the community:

- name: Install and set Flutter version
  uses: subosito/flutter-action@v1.4.0
    flutter-version: '2.0.1'

Install the Keystore

To access your keystore certificate for your application you need to use something call secrets. This allows you to store these critical files securely. If you haven’t used secrets before, checkout this previous article.

Let’s define one for:

  • the keystore file called KEYSTORE_BASE64
  • the keystore password: KEYSTORE_PASSWORD
  • the associated alias password: KEYSTORE_PASSWORD_ALIAS

With our secrets setup, we can retrieve the keystore file in our workflow like this:

- name: Create the Keystore
    KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
  run: |
    # import keystore from secrets
    echo $KEYSTORE_BASE64 | base64 -d > $RUNNER_TEMP/my_production.keystore

Build the application

We need to restore the packages of our application to be able to compile the Flutter application:

- name: Restore packages
  run: flutter pub get

It’s time to build the Flutter Android application in release mode without signing it and generate our first Android App Bundle:

- name: Build Android App Bundle
  run: flutter build appbundle --release

Sign the application

If you try to sign your application directly you will get an error saying that you try to sign a package which is already signed. Why is that?

The answer is in the build.gradle inside the app folder of the Android platform folder. By default you have something like this:

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

As you can see above the signing config is set to debug so your application is automatically signed with a debug certificate. To solve this issue, the default signingConfig should be unspecified like this:

buildTypes {
    release {
       signingConfig null

With this adjustment we can now use jarsigner to sign our Android App Bundle package:

- name: Sign Android App Bundle
  run: jarsigner -keystore $RUNNER_TEMP/my_production.keystore -storepass ${{ secrets.KEYSTORE_PASSWORD }} -keypass ${{ secrets.KEYSTORE_PASSWORD_ALIAS }} -sigalg SHA256withRSA -digestalg SHA-256 -signedjar build/app/outputs/bundle/release/app-release-signed.aab build/app/outputs/bundle/release/*.aab YOUR_PRODUCTION_ALIAS

Publish the artefacts

To get access to the Android App Bundle generated previously let’s add this final action:

- name: Publish Android Artefacts
  uses: actions/upload-artifact@v1
    name: release-aab-signed
    path: build/app/outputs/bundle/release/app-release-signed.aab

This will publish your Android App Bundle which will automatically be put in a downloadable Zip. Then you can download it and distribute it to your users.

Publish Artefacts

Final touch

Now you are ready to share your application with your users! You will find an example code in this Github repository.

Do not hesitate to follow me on to not miss my next tutorial!