Flutter Interview Handbook

CI/CD & Automation

100%

CI/CD & Automation

Enterprise mobile engineering requires automated Continuous Integration and Continuous Deployment (CI/CD) pipelines (using GitHub Actions, Fastlane, or Codemagic) to enforce code formatting, static analysis, unit testing, automated code signing, and multi-track deployment to TestFlight and Google Play.


1. End-to-End Flutter CI/CD Pipeline Flow

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     1. Git Push / PR      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚ Developer Workβ”‚ ────────────────────────► β”‚                   CI RUNNER PIPELINE                   β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚                                                        β”‚
                                             β”‚  Phase 1: Lint & Format (`flutter analyze`)            β”‚
                                             β”‚  Phase 2: Unit/Widget Tests (`flutter test`)           β”‚
                                             β”‚  Phase 3: Code Signing (Fastlane Match / Keystore)     β”‚
                                             β”‚  Phase 4: Artifact Build (`.aab` / `.ipa`)             β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                         β”‚
                                                                         β”‚ 2. Automated Release Deploy
                                                                         β–Ό
                                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                             β”‚                 DISTRIBUTION CHANNELS                  β”‚
                                             β”‚  - iOS TestFlight / App Store Connect                  β”‚
                                             β”‚  - Android Google Play Console (Internal/Production)   β”‚
                                             β”‚  - Firebase App Distribution                           β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. GitHub Actions Workflow Configuration

name: Flutter CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          channel: 'stable'

      - name: Install Dependencies
        run: flutter pub get

      - name: Check Code Formatting
        run: dart format --set-exit-if-changed .

      - name: Run Static Analyzer
        run: flutter analyze

      - name: Run Unit & Widget Tests
        run: flutter test --coverage

  deploy_android:
    needs: validate
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          channel: 'stable'

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

      - name: Build Android App Bundle (AAB)
        run: flutter build appbundle --release
        env:
          KEYSTORE_PATH: keystore.jks
          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}

      - name: Deploy to Google Play Internal Track via Fastlane
        run: cd android && bundle exec fastlane deploy_internal

3. Automated Code Signing Mechanics

1. iOS Code Signing: Fastlane Match

Managing iOS certificates and provisioning profiles across multiple developers and headless CI runners is notoriously complex.

Fastlane Match solves this by storing encrypted iOS certificates and provisioning profiles in a private, dedicated Git repository.

  • CI Execution: Fastlane decrypts the certificates using a passphrase (MATCH_PASSWORD) and installs them onto the macOS CI runner automatically (fastlane match appstore --readonly).

2. Android Code Signing: Base64 Keystore Ingestion

Store the release Java Keystore (.jks file) as a Base64-encoded environment secret (ANDROID_KEYSTORE_BASE64) in GitHub Secrets or Codemagic.

  • CI Execution: The pipeline decodes the Base64 string back into keystore.jks at runtime and injects keystore credentials via environment variables during flutter build appbundle.

4. Multi-Track Release Deployment via Fastlane

# android/fastlane/Fastfile
default_platform(:android)

platform :android do
  desc "Submit Android App Bundle to Google Play Internal Track"
  lane :deploy_internal do
    upload_to_play_store(
      track: 'internal',
      aab: '../build/app/outputs/bundle/release/app-release.aab',
      json_key_data: ENV['GCP_PLAY_STORE_JSON_KEY']
    )
  end
end

5. Trade-offs & Production Considerations

  • macOS Runner Costs: macOS CI runners (required for iOS builds) are 10x more expensive per minute than Linux runners on GitHub Actions. Run linting, static analysis, and unit tests on cheap Linux runners (ubuntu-latest), invoking macOS runners (macos-latest) ONLY for final iOS compilation and code signing.
  • Secrets Hygiene: Never commit plain text .jks keystores, .p12 certificates, or Google Play JSON API keys to Git repositories. Always inject credentials through CI secret environment variables.