---
title: Build Flutter APK via GitHub Actions (no local SDK)
description: |
  When local machine cannot build Flutter (missing Xcode/CLT, no Flutter SDK, no Android SDK),
  create a GitHub repo, push code via REST API, run Actions workflow, and download the artifact.
  Covers: repo creation, file upload via Contents API, workflow dispatch, Flutter version matching,
  project structure fixes, dependency cleanup, artifact download.
triggers:
  - "Need to build Flutter APK but can't install Flutter locally"
  - "No Xcode/CommandLineTools, can't run flutter build"
  - "Want CI build for Flutter app"
  - "Git not working locally, need to push to GitHub"
name: flutter-apk-ci-build
---

# Build Flutter APK via GitHub Actions (No Local SDK)

## Context
Use when the local machine cannot build a Flutter app (missing Xcode/CLT, no Flutter SDK, no Android SDK). Instead, push code to GitHub and let Actions build the APK, then download the artifact.

## Prerequisites
- GitHub Personal Access Token (PAT) with `repo` scope
- `curl` available locally

## Steps

### 1. Create GitHub Repository via API
```bash
curl -s -X POST \
  -H "Authorization: token <PAT>" \
  -H "Accept: application/vnd.github.v3+json" \
  -d '{"name":"<repo-name>","description":"...","private":false,"license_template":"mit"}' \
  https://api.github.com/user/repos
```

### 2. Upload Files via Contents API (when git CLI unavailable)
Read file, base64-encode, PUT to contents endpoint.
```bash
# Get existing SHA if file exists
curl -s "https://api.github.com/repos/<owner>/<repo>/contents/<path>"

# Upload
curl -s -X PUT \
  -H "Authorization: token <PAT>" \
  -H "Accept: application/vnd.github.v3+json" \
  -d '{"message":"...","content":"<base64>","sha":"<existing-sha>"}' \
  https://api.github.com/repos/<owner>/<repo>/contents/<path>
```
Python helper available to batch-upload tree items via blob+tree API for initial commit.

### 3. Create Workflow `.github/workflows/build.yml`
Key settings:
- **Flutter version must match `pubspec.yaml` SDK constraint** (`environment.sdk`).
  - Example: `sdk: ^3.5.0` → Flutter `3.24.x`, NOT `3.19.0`.
- If project lacks native Android/Gradle files (was created manually), add step:
  ```yaml
  - run: |
      cd flutter_app
      flutter create . --platforms=android
      flutter pub get
  ```
- Build: `flutter build apk --release`
- Upload artifact with `actions/upload-artifact@v4`

### 4. Clean Unused Dependencies Before Build
If the project previously included server-side/audio features that are now removed, **strip them from `pubspec.yaml`** before CI:
- `record`, `audioplayers`, `permission_handler` — cause Gradle failures if native plugins expect missing config.
- `web_socket_channel` — only if WebSocket server code removed.
- `image_picker` — only if camera/gallery features removed.
- Remove unused `assets:` entries pointing to deleted folders.

### 5. Trigger Workflow
```bash
curl -s -X POST \
  -H "Authorization: token <PAT>" \
  -H "Accept: application/vnd.github.v3+json" \
  -d '{"ref":"main"}' \
  https://api.github.com/repos/<owner>/<repo>/actions/workflows/build.yml/dispatches
```

### 6. Monitor Run Status
```bash
# Latest run
curl -s -H "Authorization: token <PAT>" \
  "https://api.github.com/repos/<owner>/<repo>/actions/runs?per_page=1"

# Specific run ID
curl -s -H "Authorization: token <PAT>" \
  "https://api.github.com/repos/<owner>/<repo>/actions/runs/<run-id>"
```

### 7. Download Artifact
```bash
# List artifacts for a run
ARTIFACT_URL=$(curl -s -H "Authorization: token <PAT>" \
  "https://api.github.com/repos/<owner>/<repo>/actions/runs/<run-id>/artifacts" \
  | python -c "import sys,json; print(json.load(sys.stdin)['artifacts'][0]['archive_download_url'])")

# Download
curl -s -L -H "Authorization: token <PAT>" "$ARTIFACT_URL" -o artifact.zip
unzip artifact.zip -d ./apk/
cp ./apk/*.apk ~/Desktop/<AppName>.apk
```

## Common Pitfalls
| Symptom | Cause | Fix |
|---|---|---|
| `version solving failed: requires SDK ^3.5.0` | Flutter version too old in workflow | Bump `flutter-version` to `3.24.5` or newer |
| `unsupported Gradle project` / missing `android/` | Project created manually without native files | Add `flutter create . --platforms=android` step |
| `Could not get unknown property 'flutter'` in plugin | Native plugin (`record`) expects Flutter tooling that conflicts with generated project | Remove unused native plugins from `pubspec.yaml` |
| `compileSdkVersion is not specified` | Plugin requires Android SDK config that's missing | Same as above — remove offending plugin or add `compileSdkVersion` to `android/app/build.gradle` |

## Verification
- APK file appears on Desktop (or chosen path)
- `flutter build apk --release` log in Actions shows `BUILD SUCCESSFUL`
- Artifact size is reasonable (~15-40 MB for a simple app)

## Example Workflow
```yaml
name: Build APK
on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.5'
          channel: 'stable'
      - run: |
          cd flutter_app
          flutter create . --platforms=android
          flutter pub get
      - run: |
          cd flutter_app
          flutter build apk --release
      - uses: actions/upload-artifact@v4
        with:
          name: app-release
          path: flutter_app/build/app/outputs/flutter-apk/app-release.apk
```
