# Flutter Build

> When to activate: Flutter build, flavors, build variants, CI/CD, fastlane, GitHub Actions, signing, shorebird, release, deployment

- Skill: `mattakushi432/flutter-build` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/flutter-build`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/flutter-build/raw
- Safety review: WARNING
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/flutter-build

---

# Flutter Build & Deployment Patterns

## Flavors (Build Variants)

```dart
// lib/main_dev.dart
void main() => runApp(const App(flavor: Flavor.dev));

// lib/main_prod.dart
void main() => runApp(const App(flavor: Flavor.prod));

// lib/flavor.dart
enum Flavor { dev, staging, prod }

class FlavorConfig {
  static late final Flavor current;
  static bool get isDev => current == Flavor.dev;

  static String get apiBaseUrl => switch (current) {
    Flavor.dev     => 'https://dev-api.example.com',
    Flavor.staging => 'https://staging-api.example.com',
    Flavor.prod    => 'https://api.example.com',
  };
}
```

```bash
# Run with flavor
flutter run --flavor dev -t lib/main_dev.dart
flutter run --flavor prod -t lib/main_prod.dart

# Build
flutter build apk --flavor prod -t lib/main_prod.dart --release
flutter build ipa --flavor prod -t lib/main_prod.dart --release
```

## Android Signing

```groovy
// android/app/build.gradle
android {
    signingConfigs {
        release {
            keyAlias System.getenv("KEY_ALIAS")
            keyPassword System.getenv("KEY_PASSWORD")
            storeFile file(System.getenv("KEYSTORE_PATH") ?: "keystore.jks")
            storePassword System.getenv("STORE_PASSWORD")
        }
    }
    buildTypes {
        release { signingConfig signingConfigs.release }
    }
}
```

## iOS Signing (fastlane match)

```ruby
# Matchfile
git_url("https://github.com/org/certificates")
storage_mode("git")
type("appstore")
app_identifier(["com.example.app"])

# Fastfile
lane :beta do
  match(type: "appstore")
  build_ios_app(
    workspace: "Runner.xcworkspace",
    configuration: "Release-prod",
    export_method: "app-store"
  )
  upload_to_testflight
end
```

## GitHub Actions CI/CD

```yaml
# .github/workflows/flutter.yml
name: Flutter CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          cache: true
      - run: flutter pub get
      - run: flutter analyze
      - run: flutter test --coverage
      - uses: codecov/codecov-action@v4
        with:
          file: coverage/lcov.info

  build-android:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          cache: true
      - name: Decode keystore
        run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/keystore.jks
      - run: flutter build apk --flavor prod -t lib/main_prod.dart --release
        env:
          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
          STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
          KEYSTORE_PATH: keystore.jks
      - uses: actions/upload-artifact@v4
        with:
          name: release-apk
          path: build/app/outputs/apk/prod/release/*.apk

  build-ios:
    needs: test
    runs-on: macos-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          cache: true
      - run: flutter build ipa --flavor prod -t lib/main_prod.dart --release --no-codesign
      - uses: actions/upload-artifact@v4
        with:
          name: release-ipa
          path: build/ios/ipa/*.ipa
```

## Shorebird (OTA Code Push)

```bash
# Install
curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bash

# Initialize
shorebird init

# Release (uploads to Shorebird CDN)
shorebird release android --flavor prod -t lib/main_prod.dart
shorebird release ios --flavor prod -t lib/main_prod.dart

# Patch (OTA update — no store review)
shorebird patch android --flavor prod
shorebird patch ios --flavor prod
```

## dart_defines for Environment Variables

```bash
# Pass at build time (safer than .env files)
flutter run \
  --dart-define=API_URL=https://api.example.com \
  --dart-define=SENTRY_DSN=https://xxx@sentry.io/123

# In Dart code
const apiUrl = String.fromEnvironment('API_URL', defaultValue: 'http://localhost:8080');
const sentryDsn = String.fromEnvironment('SENTRY_DSN');
```

## Obfuscation & Minification

```bash
# Android
flutter build apk --release --obfuscate --split-debug-info=build/debug-info/android

# iOS
flutter build ipa --release --obfuscate --split-debug-info=build/debug-info/ios

# Upload debug symbols to Sentry
sentry-cli upload-dif build/debug-info/
```

## Version Management

```yaml
# pubspec.yaml
version: 2.4.1+47  # semver+buildNumber

# Auto-increment in CI
# Use flutter_version or cider package, or sed:
# sed -i "s/version:.*/version: $VERSION+$BUILD_NUMBER/" pubspec.yaml
```

## Build Checklist

- [ ] Flavors configured for dev/staging/prod
- [ ] Signing keys in CI secrets, not committed
- [ ] `flutter analyze` passes with zero errors
- [ ] All tests pass (`flutter test`)
- [ ] `--obfuscate` enabled for release builds
- [ ] Version bumped in `pubspec.yaml`
- [ ] Debug logs / print statements removed
- [ ] Release notes prepared for store submission

