CI/CD Tutorial: Fastlane + GitHub Actions for Flutter Android
Overview
The pipeline does the following on every manual trigger:
- Decodes secrets (keystore, service account) from GitHub Secrets
- Sets up Java, Flutter, and Ruby environments
- Runs Fastlane to auto-increment the build number, build the AAB + APK, and upload to Play Store (internal track)
- Uploads artifacts to GitHub Actions
- Creates a GitHub Release with the AAB and APK attached
Prerequisites
- A Flutter Android project
- A Google Play Console account with your app created (at least one manual upload done)
- A Google Cloud Platform (GCP) project
- A signing keystore for your Android app
- Ruby installed locally (for running fastlane init)
Step 1: Create a Keystore for App Signing
If you don't already have a keystore, generate one:
keytool -genkey -v -keystore release-keystore.jks -alias your-key-alias -keyalg RSA -keysize 2048 -validity 10000
Keep note of:
- storePassword — the keystore password
- keyPassword — the key password
- keyAlias — the alias you chose
Do not commit the .jks file. Add it to .gitignore.
Step 2: Configure key.properties in Android
Create android/key.properties (also add to .gitignore):
storePassword=YOUR_STORE_PASSWORD keyPassword=YOUR_KEY_PASSWORD keyAlias=YOUR_KEY_ALIAS storeFile=release-keystore.jks
In android/app/build.gradle.kts, load and use this file for signing:
val keystorePropertiesFile = rootProject.file("key.properties") val keystoreProperties = Properties() if (keystorePropertiesFile.exists()) { keystoreProperties.load(FileInputStream(keystorePropertiesFile)) }
signingConfigs { create("release") { keyAlias = keystoreProperties.getProperty("keyAlias") keyPassword = keystoreProperties.getProperty("keyPassword") storeFile = keystoreProperties.getProperty("storeFile")?.let { file(it) } storePassword = keystoreProperties.getProperty("storePassword") } }
buildTypes { release { signingConfig = signingConfigs.getByName("release") } }
Step 3: Set Up GCP Service Account
Fastlane uses a Google Play API service account to upload builds.
- Go to Google Cloud Console
- Create a new project (or use an existing one)
- Enable the Google Play Android Developer API
- Go to IAM & Admin → Service Accounts → Create Service Account
- Give it a name (e.g., fastlane-cicd)
- Grant it no roles at the project level
- Create and download a JSON key for this service account
Grant Access in Play Console
- Open Google Play Console
- Go to Setup → API access
- Link to the GCP project you created
- Find your service account, click Grant Access
- Assign the Release Manager role
Step 4: Initialize Fastlane
From the android/ directory:
cd android fastlane init
When prompted, choose Google Play as the deployment target and provide your package name.
This generates:
- android/Gemfile
- android/fastlane/Appfile
- android/fastlane/Fastfile
Step 5: Configure Fastlane Files
android/Gemfile
source "https://rubygems.org" gem "fastlane"
android/fastlane/Appfile
json_key_file("~/fastlane/service_account.json") package_name("com.example.yourapp")
android/fastlane/Fastfile
Fastlane automatically:
- Fetches the highest version code across all tracks
- Increments build number automatically
- Builds APK and AAB files
- Uploads to internal testing track
- Writes build_number.txt for GitHub Releases
Key points:
- No manual version bumping needed
- Internal track upload by default
- Faster uploads by skipping metadata/images/screenshots
Step 6: Add GitHub Secrets
Go to GitHub → Settings → Secrets and variables → Actions.
Add:
- ANDROID_KEYSTORE
- SERVICE_ACCOUNT
- KEYSTORE_PASSWORD
- KEY_PASSWORD
- KEY_ALIAS
Use Base64 encoding for keystore and service account files.
Step 7: GitHub Actions Workflow
Create .github/workflows/deploy_android.yml.
The workflow:
- Checks out repository
- Sets up Java, Flutter, and Ruby
- Decodes secrets
- Runs Fastlane deploy
- Uploads APK/AAB artifacts
- Creates GitHub Release automatically
Notable Decisions
- workflow_dispatch allows manual release triggering
- continue-on-error keeps artifact upload running even if Play Store upload fails
- permissions: contents: write is required for GitHub Releases
- working-directory: android ensures Fastlane runs correctly
File Structure Summary
project-root/ ├── .github/ │ └── workflows/ │ └── deploy_android.yml ├── android/ │ ├── Gemfile │ ├── Gemfile.lock │ ├── key.properties │ ├── app/ │ │ ├── build.gradle.kts │ │ └── release-keystore.jks │ └── fastlane/ │ ├── Appfile │ └── Fastfile └── pubspec.yaml
Triggering a Release
- Go to GitHub Actions
- Select Deploy Flutter Android
- Click Run workflow
The pipeline will:
- Auto-detect next versionCode
- Build APK and AAB
- Upload to Play Store internal track
- Create GitHub Release
Supply (upload_to_play_store) Reference
Available tracks:
- internal
- alpha
- beta
- production
Key parameters:
- track
- aab
- rollout
- release_status
- track_promote_to
Fastlane also supports:
- Gradual rollouts
- Track promotion
- Metadata downloads
- Screenshot management
Important: First Upload Must Be Manual
The Play Store API cannot push to an app that has never had a build uploaded manually through Play Console.
Common Issues
Google Api Error
Only releases with status draft may be created on draft app.
Solution: Complete at least one manual Play Store upload first.
Version code already exists
Re-run the workflow to get a fresh build number.
base64: invalid input
Encode files without line breaks:
- Linux: base64 -w 0 file
- macOS: base64 -i file
Pro-Tip: Optimize App Store Screenshots and Assets with DocShrink
When deploying to the Google Play Store, minimizing your app size is crucial for download conversion. You can compress your app's image assets and screenshots using DocShrink's AI Photo Compressor by up to 90% without any visible quality loss. If you're building products that require transparent background product images or clean App Store screenshots, check out DocShrink's AI Background Remover to remove backgrounds instantly right in your browser.
Conclusion
Using Fastlane with GitHub Actions creates a powerful CI/CD pipeline for Flutter Android apps. It automates versioning, building, Play Store deployment, artifact uploads, and GitHub Releases while reducing manual work and deployment errors.