effidevFlutter · Cloudflare edge · Cloud cost optimization
English

Automate Flutter iOS/Android Builds, Code Signing, and App Store Deployments with Fastlane + GitHub Actions

Illustration showing a CI/CD deployment pipeline with App Store and Google Play icons

Developing apps with Flutter is a joy, but the process of deploying the finished app to the App Store and Google Play Store often causes significant stress for developers. Managing certificates, matching provisioning profiles, bumping build versions, and uploading to the market require far too many manual steps.

This guide covers how to combine Fastlane and GitHub Actions to fully automate the iOS and Android builds, code signing, and store deployment processes for your Flutter app.

Why Do We Need CI/CD Automation?

The following issues frequently occur during the manual deployment process:

By using GitHub Actions and Fastlane, you can securely handle all these processes in the cloud simply by pushing code to the main or release branch.


1. Fastlane Setup (Local)

First, you need to initialize Fastlane in the android and ios directories of your Flutter project, respectively.

Android Fastlane Initialization

cd android
fastlane init

During the initialization process, it will ask for the package name and the path to the JSON secret key. You need the Service Account JSON key issued from the Google Play Console.

Example android/fastlane/Fastfile:

default_platform(:android)

platform :android do
  desc "Submit a new Beta Build to Crashlytics"
  lane :beta do
    gradle(task: "clean bundleRelease")
    upload_to_play_store(track: 'beta')
  end
end

iOS Fastlane Initialization

For iOS deployment, managing certificates and provisioning profiles is crucial. Using Fastlane’s match allows you to securely share this process with team members via a Git repository and easily load it in CI/CD.

cd ios
fastlane init
fastlane match init

Example ios/fastlane/Fastfile:

default_platform(:ios)

platform :ios do
  desc "Push a new beta build to TestFlight"
  lane :beta do
    setup_ci
    match(type: "appstore", readonly: true)
    
    # Since Flutter builds are done in GitHub Actions, Fastlane handles the already built app.
    build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
    upload_to_testflight
  end
end

[!IMPORTANT] To execute iOS code signing in a CI environment, using Fastlane Match is highly recommended. It is much safer and easier to manage than exporting certificates locally and uploading them to CI.


2. Configuring the GitHub Actions Workflow

Now, create a .github/workflows/deploy.yml file at the root of the repository.

Basic Workflow Structure and Environment Variables

You must save the following values in GitHub Secrets in advance:

Full Workflow Script

name: Deploy to App Store & Google Play

on:
  push:
    tags:
      - 'v*' # Runs when pushing tags like v1.0.0

jobs:
  build-and-deploy:
    runs-on: macos-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '17'
          
      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          channel: 'stable'
          
      - name: Install dependencies
        run: flutter pub get
        
      # --- iOS Deployment Pipeline ---
      - name: Setup Ruby for Fastlane
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2'
          bundler-cache: true
          working-directory: ios
          
      - name: Decode App Store Connect API Key
        run: |
          mkdir -p ~/.appstoreconnect/private_keys/
          echo "${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}" > ~/.appstoreconnect/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}.p8
          
      - name: Build iOS App
        run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
        
      - name: Deploy iOS to TestFlight
        working-directory: ios
        env:
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
        run: bundle exec fastlane beta
        
      # --- Android Deployment Pipeline ---
      - name: Setup Ruby for Fastlane (Android)
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2'
          bundler-cache: true
          working-directory: android
          
      - name: Decode Play Store JSON
        run: echo "${{ secrets.PLAY_STORE_CONFIG_JSON }}" > android/play_store_config.json
        
      - name: Build Android App
        run: flutter build appbundle --release
        
      - name: Deploy Android to Play Store
        working-directory: android
        run: bundle exec fastlane beta

Key Considerations

  1. Mac Environment Selection: You must use the macos-latest runner for iOS builds. (Note that billing rates are higher than Linux runners.)
  2. Flutter Build Optimization: For iOS, the flutter build ipa command can handle everything up to archiving. In this case, configuring Fastlane to only handle the upload role is advantageous for speed.
  3. API Key Authentication: The traditional Apple ID/password method is very tricky to use in CI environments due to 2FA. Be sure to issue and use an App Store Connect API Key.

Troubleshooting Guide

Symptom Cause and Solution
Missing private key for ... (iOS) Unable to access the Fastlane Match repository, or the MATCH_PASSWORD is incorrect. Check the Secret values.
Google Api Error: Invalid request - Invalid package name (Android) Occurs when the app has never been registered in the Google Play Console. The very first deployment must be uploaded manually as an AAB.
Code signing is required for product type 'Application' (iOS) The signing setting in the Xcode project file (.pbxproj) is set to ‘Automatic’, and the provisioning profile is not injected. Change the project settings to use Match.

Conclusion

Building a CI/CD pipeline may take half a day for initial setup, but once established, it saves a tremendous amount of time and stress over dozens or hundreds of subsequent deployments. Especially in team development, it positively transforms the development culture by eliminating the worry of ‘who will deploy.’

Introduce Fastlane and GitHub Actions to your project right now and create an environment where you can focus solely on development.