If you’ve typed `uses: subosito/flutter-action@v2` into a workflow file, you’ve already found the action almost every Flutter project on GitHub relies on to install the SDK on a runner. What trips people up isn’t finding it — it’s the details: which inputs actually exist, why `flutter-version: stable` throws an error, what the outputs are called, and how to fix the SDK failing to resolve in CI when everything works fine locally.
Table of Contents
This is a complete reference for subosito/flutter-action@v2 — every input and output, the channel vs. flutter-version distinction explained clearly, the errors people hit most (including ones the original docs barely cover, like ARM64 runners and self-hosted git ownership failures), and a full working CI/CD pipeline built around it.
Quick Answer
subosito/flutter-action@v2 is the standard GitHub Action for installing the Flutter SDK on a runner (Linux, Windows, and macOS). Add it to a job step with `channel: stable` and `cache: true`, then run `flutter pub get`, `flutter analyze`, `flutter test`, and `flutter build apk` as sequential steps. Set `flutter-version` only to pin an exact SDK build (e.g. `3.44.0`) — never set it to the word “stable,” since that’s a channel name, not a version, and the action will fail to resolve it.
What Is subosito/flutter-action@v2?
subosito/flutter-action is a community-maintained GitHub Action, and `@v2` is its current major version tag — the one referenced in essentially every Flutter CI/CD tutorial, template repo, and Stack Overflow answer you’ll find. Pinning `@v2` (rather than a full tag like `@v2.21.0`) means your workflow automatically picks up bug fixes and new Flutter releases without you editing YAML every time.
Under the hood it downloads a Flutter SDK archive that matches your requested channel/version/architecture, extracts it to the runner’s tool cache, adds it to `PATH`, and (optionally) restores a cached copy on the next run so you’re not re-downloading a multi-hundred-megabyte SDK on every push. It doesn’t run your tests or builds for you — that’s still `flutter test` and `flutter build` steps you write yourself — it just makes sure the right Flutter toolchain exists on the runner before those steps execute.
Every Input and Output, Explained
Inputs: `channel` (release track — stable, beta, dev, or master/main), `flutter-version` (an exact SDK version, a partial match like `3.x`, or on the master channel a git ref), `flutter-version-file` (reads the version from `pubspec.yaml`, `.fvmrc`, or `.fvm/fvm_config.json` instead of hardcoding it), `cache` (caches the Flutter SDK itself between runs — off by default), `pub-cache` (caches your pub dependencies separately, inheriting `cache`’s value if unset), `cache-key`/`cache-path`/`pub-cache-key`/`pub-cache-path` (customize where and how caches are stored, with dynamic tokens like `:os:`, `:channel:`, `:version:`, `:arch:`, and `:hash:`), `architecture` (target CPU architecture), `git-source` (point at a Flutter fork, such as the community Flock mirror, instead of the official repo), and `dry-run` (resolve version info without installing the SDK — useful for debugging cache keys).
Outputs, referenced in later steps as `steps.
channel vs. flutter-version: Which Values Are Valid?
`channel` selects a release track and accepts exactly four values: `stable`, `beta`, `dev`, and `master` (`main` also works as an alias). `flutter-version` expects an actual version number — a full version like `3.44.0`, a partial match like `3.x`, or, only on the master channel, a git tag, commit hash, or branch name. Setting `flutter-version: stable` fails because the action tries to resolve “stable” as a version string and can’t find a matching release — you’ll see it error out before the SDK ever installs.
The fix: either set `channel: stable` and leave `flutter-version` empty to always get the latest stable build, or set both `channel: stable` and `flutter-version: 3.44.0` together to pin an exact release while still validating it against the stable track. If you’d rather not hardcode a version at all, use `flutter-version-file: pubspec.yaml` so the action reads the exact SDK version from your pubspec’s `environment` field — that field must specify an exact version (`flutter: 3.44.0`), since version ranges aren’t resolvable by the action.
Fixing “Unable to Determine Flutter Version” and Other Common Errors
“Unable to determine Flutter version for channel: X version: Y architecture: Z” almost always traces back to one of three causes: an invalid `flutter-version` value (see above), a requested version/architecture combination that Google hasn’t published a build for (this happens with brand-new hotfix releases on ARM64 before the corresponding artifact ships), or a stale `dry-run` cache masking a typo in your inputs. Start by removing `flutter-version` entirely and letting `channel` alone resolve the latest build — if that works, the problem is in how you specified the version.
On self-hosted runners you may also hit `fatal: detected dubious ownership in repository`, a Git security check that trips when the runner user doesn’t own the checked-out or cached SDK directory. Add a step before flutter-action that runs `git config –global –add safe.directory
subosito/flutter-action vs. Alternatives
subosito/flutter-action remains the default choice and what most existing templates use, but if you hit a wall the action hasn’t fixed yet, `flutter-actions/setup-flutter` is a newer, actively maintained alternative worth knowing about. Migration isn’t a straight copy-paste of input names: subosito/flutter-action’s `flutter-version` input becomes `version` in setup-flutter, while `channel` carries over directly. Cache behavior is comparable rather than different — setup-flutter exposes separate `cache` (pub) and `cache-sdk` (SDK) booleans that both default to `false`, the same off-by-default posture as subosito/flutter-action’s single `cache` input; you have to explicitly opt in to caching in either action. For most projects, stick with subosito/flutter-action@v2 unless you’re specifically blocked on a maintenance-related bug.
Step-by-Step: Building the Workflow File
Create `.github/workflows/flutter-ci.yml` in your repo. Trigger it with `on: push` and `on: pull_request` targeting your default branch, choose `runs-on: ubuntu-latest` for Android/web builds (use `macos-latest` for iOS or macOS-desktop), then add the action: `uses: subosito/flutter-action@v2` with `with: channel: stable` and `cache: true`. Follow it with `flutter pub get`, `flutter analyze`, `flutter test`, and `flutter build apk –release` (or `flutter build ios –release –no-codesign` for an unsigned iOS build) as separate `run:` steps so failures are attributed to the right stage in the Actions log.
For matrix builds across Android and iOS in one workflow, define a `strategy.matrix` with `os: [ubuntu-latest, macos-latest]` and reference `${{ matrix.os }}` in `runs-on` — each OS gets its own job with the flutter-action step re-run per runner, since the SDK cache doesn’t share across operating systems.
Adding Deployment and Secrets
Store your Android keystore (base64-encoded) and Apple certificates/provisioning profiles as GitHub Encrypted Secrets — never commit them. Decode the keystore in a step before `flutter build appbundle`, reference the secrets via `${{ secrets.YOUR_SECRET_NAME }}`, and hand the signed artifact to a deployment action like `wzieba/Firebase-Distribution-Github-Action` for Firebase App Distribution, or a fastlane lane for TestFlight/Play Store uploads. Gate deployment steps with `if: github.ref == ‘refs/heads/main’` so only merges to your release branch actually ship a build.
subosito/flutter-action@v2 FAQs
What is subosito/flutter-action@v2?
It’s a GitHub Action that installs the Flutter SDK on a runner (Linux, Windows, or macOS) so subsequent workflow steps can run flutter commands. @v2 is its current major version tag, and it’s the de facto standard used across nearly all Flutter CI/CD workflows on GitHub.
Is flutter-version: stable a valid value?
No. “stable” is a channel name, not a version number. Use channel: stable instead, and leave flutter-version empty (for the latest build) or set it to an exact version like 3.44.0 (to pin a release on that channel).
Which Flutter GitHub Action should I use?
subosito/flutter-action@v2 is the safe default — it’s what most templates and tutorials use. Consider flutter-actions/setup-flutter only if you need more granular cache control or hit a subosito/flutter-action bug that hasn’t been patched yet; migrating between the two means renaming flutter-version to version, since input names aren’t identical.
How do I pin an exact Flutter version instead of always using the latest stable?
Set both channel: stable and flutter-version: 3.44.0 (or whatever exact version you need) in the action’s with: block. Setting flutter-version alone without a channel also works but is less explicit.
Can I build for both Android and iOS in the same workflow file?
Yes, using a build matrix with os: [ubuntu-latest, macos-latest] under strategy.matrix, referencing ${{ matrix.os }} in runs-on. Each OS runs the flutter-action step and build commands independently since caches don’t share across operating systems.
How do I make the pipeline run only on pull requests and not on every push?
Remove the push trigger and keep only on: pull_request in your workflow’s on: block, optionally scoped to specific branches with pull_request: branches: [main].
Why is my flutter test step failing in CI but passing locally?
Usually a Flutter/Dart version mismatch — pin flutter-version or flutter-version-file to match your local SDK exactly. Other common causes are missing generated files (run build_runner in CI too) or platform-specific test dependencies not installed on the runner.
How do I fix “Unable to determine Flutter version for channel: stable”?
Remove flutter-version and rely on channel alone first to confirm the action itself works. If the error is specific to a version/architecture combo, that build may not exist yet for your target architecture (common with brand-new releases on ARM64) — try a slightly older pinned version.
How do I fix “detected dubious ownership in repository” on a self-hosted runner?
Add a step before flutter-action that runs git config –global –add safe.directory
Get More from subosito/flutter-action@v2
Log the coasters, stadiums, and venues you’ve experienced, rate subosito/flutter-action@v2, and see what your friends thought. Get the ThrillZing app.