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
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
Generate it in your workflow:
- name: Generate key.properties
run: |
echo "${{ secrets.KEY_PROPERTIES }}" > android/key.properties
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
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
Firebase Configuration
Generate a Firebase CI token using:
firebase login:ci
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 }}
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 tomain
, and when apull request
targets main. -
jobs
contains the tasks the workflow will run. Here, there is a single job named build. -
build
runs onubuntu-latest
and handles the Flutter build and distribution process. -
steps
are the tasks executed in order:
- Checkout your repository code.
- Set up Flutter using the stable channel.
- Generate the
.env
andkey.properties
files from GitHub Secrets. - Decode your
keystore
for signing. - Install dependencies with
flutter pub get
. - Build a release APK.
- Install the Firebase CLI.
- 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.
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)