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:
-
.envfiles containing API keys or environment configurations. -
android/app/upload-keystore.jksor any keystore used for signing. -
android/key.propertiescontaining keystore passwords and paths. -
google-services.jsonand 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.
-
ondefines when the workflow runs. Here, it triggers manually (workflow_dispatch), on every push tomain, and when apull requesttargets main. -
jobscontains the tasks the workflow will run. Here, there is a single job named build. -
buildruns onubuntu-latestand handles the Flutter build and distribution process. -
stepsare the tasks executed in order:
- Checkout your repository code.
- Set up Flutter using the stable channel.
- Generate the
.envandkey.propertiesfiles from GitHub Secrets. - Decode your
keystorefor 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)