DEV Community

Cover image for Setting Up Flutter CI/CD with GitHub Actions and Firebase App Distribution Securely
Mambaur Roziq
Mambaur Roziq

Posted on

Setting Up Flutter CI/CD with GitHub Actions and Firebase App Distribution Securely

Automating Flutter app build and distribution saves time, enforces consistency, and enables rapid delivery to testers and users. This guide will help you set up a secure Flutter CI/CD pipeline using GitHub Actions and Firebase App Distribution while keeping sensitive files like keystores and environment files out of your repository.

Why Automate Flutter CI/CD?

Building and distributing manually introduces inconsistency, human error, and repetitive overhead, especially when working in teams or maintaining frequent updates. By using CI/CD:

  • You automatically build release APK/AAB files on each push or pull request.
  • Builds are consistent across environments.
  • You can distribute builds to testers automatically via Firebase App Distribution.
  • You keep sensitive files secure and out of version control.

Files You Should Not Commit to Git

To keep your app secure, the following files should remain out of your repository:

  • .env files containing API keys or environment configurations.
  • android/app/upload-keystore.jks or any keystore used for signing.
  • android/key.properties containing keystore passwords and paths.
  • google-services.json and GoogleService-Info.plist (optional but recommended).

Managing Sensitive Files with GitHub Secrets

Since GitHub Actions cannot access files ignored by Git, we use GitHub Secrets and base64 encoding to handle these files securely during CI/CD.

To add GitHub Secrets for your Flutter CI/CD, go to your repository on GitHub, click Settings at the top, then select Secrets and variables in the left sidebar under the Security section. Choose the Actions tab and click New repository secret.

Storing .env

Copy your entire .env file content and save it under ENV_FILE in your GitHub Secrets.

In your workflow:

- name: Generate .env file
  run: |
    echo "${{ secrets.ENV_FILE }}" > .env
Enter fullscreen mode Exit fullscreen mode

Handling key.properties

Save your key.properties content to a secret named KEY_PROPERTIES. Ensure the storeFile path matches where you will place your keystore during CI/CD. For example:

storePassword=your_store_password
keyPassword=your_key_password
keyAlias=your_key_alias
storeFile=your-keystore.jks
Enter fullscreen mode Exit fullscreen mode

Generate it in your workflow:

- name: Generate key.properties
  run: |
    echo "${{ secrets.KEY_PROPERTIES }}" > android/key.properties
Enter fullscreen mode Exit fullscreen mode

Managing the Keystore File

Since GitHub Secrets do not accept files, convert your keystore to base64 on your local machine:

base64 upload-keystore.jks > upload-keystore.jks.base64
Enter fullscreen mode Exit fullscreen mode

Copy the output and save it in GitHub Secrets as KEYSTORE_BASE64.

In your workflow, decode it before the build step:

- name: Decode keystore
  run: |
    echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/your-keystore.jks
Enter fullscreen mode Exit fullscreen mode

Firebase Configuration

Generate a Firebase CI token using:

firebase login:ci
Enter fullscreen mode Exit fullscreen mode

Store it in GitHub Secrets as FIREBASE_TOKEN.

Writing Your GitHub Actions Workflow

Create a workflow file, for example:

.github/workflows/flutter-firebase-distribution.yml

name: Build & Distribute Flutter App

on:
  workflow_dispatch:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    name: Build and distribute Android
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source
        uses: actions/checkout@v4

      - name: Set up Flutter
        uses: subosito/flutter-action@v2
        with:
          channel: stable

      - name: Generate .env file
        run: |
          echo "${{ secrets.ENV_FILE }}" > .env

      - name: Generate key.properties
        run: |
          echo "${{ secrets.KEY_PROPERTIES }}" > android/key.properties

      - name: Decode keystore
        run: |
          echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/your-keystore.jks

      - name: Install dependencies
        run: flutter pub get

      - name: Build APK
        run: flutter build apk --release

      - name: Install Firebase CLI
        run: npm install -g firebase-tools

      - name: Upload to Firebase App Distribution
        run: |
          firebase appdistribution:distribute build/app/outputs/flutter-apk/app-release.apk \
            --app <App ID> \
            --release-notes "Automated build from GitHub Actions" \
            --groups "tester-group"
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

This workflow automates building and distributing your Flutter app using GitHub Actions.

  • on defines when the workflow runs. Here, it triggers manually (workflow_dispatch), on every push to main, and when a pull request targets main.
  • jobs contains the tasks the workflow will run. Here, there is a single job named build.
  • build runs on ubuntu-latest and handles the Flutter build and distribution process.
  • steps are the tasks executed in order:
  1. Checkout your repository code.
  2. Set up Flutter using the stable channel.
  3. Generate the .env and key.properties files from GitHub Secrets.
  4. Decode your keystore for signing.
  5. Install dependencies with flutter pub get.
  6. Build a release APK.
  7. Install the Firebase CLI.
  8. Upload the APK to Firebase App Distribution for testers.

This workflow ensures that every push or pull request to main automatically builds your Flutter app and distributes it to your testers without manual intervention.

How to Start and Monitor Your Flutter CI/CD

To start your Flutter CI/CD pipeline, you can trigger the workflow automatically by pushing commits to the main branch or by creating or updating a pull request targeting main. You can also run it manually using the “Run workflow” button in the Actions tab on your GitHub repository, which uses workflow_dispatch.

Once triggered, GitHub Actions will automatically start building your Flutter app, following the steps defined in your workflow file. You can monitor the progress and logs of each step in real time by going to the Actions tab in your repository, selecting the running workflow, and expanding the job and steps to see detailed output for debugging or confirming successful builds and distribution. This allows you to ensure your Flutter app builds consistently and is distributed automatically without manual steps each time you update your code.

Github Action Flutter

Summary

Setting up a secure Flutter CI/CD pipeline using GitHub Actions and Firebase App Distribution involves:

  • Keeping sensitive files out of your repository.
  • Managing secrets securely using GitHub Secrets and base64 encoding.
  • Automating build, test, and distribution processes.
  • Providing consistent, reliable builds to your testers automatically.
  • Once set up, you will save hours of manual work and ensure your Flutter app delivery process is robust, secure, and scalable.

Top comments (0)