GitHub Actions is a CI/CD (Continuous Integration and Continuous Deployment) tool built directly into GitHub. It allows developers to define workflows, which are sequences of automated steps triggered by events such as pushing code, opening pull requests, or creating releases.
For Flutter developers, GitHub Actions is a powerful way to automate testing, builds, and deployment across multiple platforms.
This guide will walk you through setting up GitHub Actions for a Flutter project, covering everything from prerequisites to detailed explanations of the workflow.
Table of Contents
Why Use GitHub Actions in Flutter Development?
GitHub Actions automated testing ensures that all code changes are validated with unit and integration tests. Continuous integration builds Flutter apps automatically to confirm that new code integrates correctly.
Code analysis and linting can run automatically to enforce style and maintain code quality. Automated releases streamline the process of packaging and distributing apps. Custom workflows can be tailored to fit project-specific needs. Collaboration is also improved because developers can see workflow results directly in pull requests.
By introducing GitHub Actions, Flutter projects become more reliable, maintainable, and efficient.
Prerequisites
Before setting up GitHub Actions for your Flutter project, make sure you have:
Flutter SDK installed locally so you can create and test the project before pushing to GitHub.
Git installed to manage version control and push your project to GitHub.
A GitHub account and a new repository created for your Flutter project.
Basic understanding of YAML syntax, since workflows are defined in
.yml
files.A GitHub personal access token (PAT) for releasing builds, which will be stored as a repository secret.
Step 1: Create a New Flutter Project
Start by creating a new Flutter project and navigating into it:
flutter create gh_flutter
cd gh_flutter
Replace gh_flutter
with your preferred project name. This initializes a Flutter project with the default structure and dependencies.
Step 2: Push the Project to GitHub
Initialize Git inside your project and push it to GitHub:
git init
git add .
git commit -m "Initial commit"
git remote add origin <repository_url>
git push -u origin main
Replace <repository_url>
with the repository URL you created on GitHub. This links your local Flutter project to GitHub, allowing GitHub Actions to run on your repository.
Step 3: Create a GitHub Actions Workflow
Inside your project, create a workflow configuration file. Workflows must be placed inside .github/workflows/
. Create a file named ci.yml
:
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
flutter_test:
name: Run Flutter Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- run: flutter pub get
- run: flutter --version
- run: flutter analyze
- run: flutter test
build_iOSApp:
name: Build Flutter App (iOS)
needs: [flutter_test]
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.19.0'
dart-verion: '3.3.4'
channel: 'stable'
- run: flutter pub get
- run: flutter clean
- run: |
flutter build ios --no-codesign
cd build/ios/iphoneos
mkdir Payload
cd Payload
ln -s ../Runner.app
cd ..
zip -r app.ipa Payload
build_androidApk:
name: Build Flutter App (Android)
needs: [flutter_test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- run: flutter pub get
- run: flutter clean
- run: flutter build apk --debug
- uses: ncipollo/release-action@v1
with:
artifacts: "build/app/outputs/apk/debug/*"
tag: v1.0.${{ github.run_number}}
token: ${{ secrets.TOKEN}}
This workflow is named CI
and is meant for Continuous Integration (running tests and building apps automatically whenever code is pushed or a pull request is created).
Triggers
In GitHub Actions, triggers define the events that cause a workflow to run. For this workflow, it runs automatically when certain events happen in the repository. Specifically, it listens to:
push
: Whenever new code is pushed to themain
branch, the workflow will start.pull_request
: Whenever a pull request is opened or updated that targets themain
branch, the workflow will also start.
This ensures that both direct updates to the main branch and contributions through pull requests are validated and tested.
on:
push:
branches:
- main
pull_request:
branches:
- main
This code runs the workflow when:
You push commits to the
main
branch.A pull request is opened or updated targeting
main
.
Jobs
There are 3 jobs in the workflow:
Job 1: flutter_test
runs unit tests and analysis.
jobs:
flutter_test:
runs-on: ubuntu-latest
It uses Ubuntu as the runner.
Here are the steps it follows:
Checks out code:
- uses: actions/checkout@v3
Downloads your repo into the runner.
Sets up Java (needed for Flutter Android builds):
- uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17'
Sets up Flutter SDK:
- uses: subosito/flutter-action@v2 with: channel: 'stable'
This installs the Flutter stable channel.
Runs commands:
flutter pub get
installs dependencies.flutter --version
checks installed Flutter version.flutter analyze
analyzes Dart code for errors.flutter test
runs unit/widget tests.
If this job fails, later jobs won’t run.
Job 2: build_iOSApp
builds an iOS .ipa
file.
build_iOSApp:
needs: [flutter_test]
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.0'
- name: Install CocoaPods dependencies
run: |
cd ios
pod install
- name: Build iOS App
run: flutter build ipa --release --no-codesign
This runs only after flutter_test
succeeds and uses macOS runner (needed for iOS builds).
After installing CocoaPods dependencies, the workflow executes flutter build ipa --release --no-codesign
. This shell command tells Flutter to package your iOS app into an .ipa
file inside the runner’s build directory. The --no-codesign
flag allows building without signing credentials, which is convenient for CI pipelines.
Here are the steps it follows:
Checks out repo + sets up Java (same as before).
Sets up Flutter but this time pins:
flutter-version: '3.19.0' dart-verion: '3.3.4' # typo: should be `dart-version` channel: 'stable'
Runs build:
flutter pub get
fetches packages.flutter clean
cleans old builds.flutter build ios --no-codesign
builds iOS app without signing.After building:
Goes into
build/ios/iphoneos
Creates a
Payload
folder (needed for IPA structure).Symlinks the generated
Runner.app
intoPayload
.Zips the folder to
app.ipa
.
Result: An unsigned .ipa
file.
Job 3: build_androidApk
builds a debug Android .apk
and uploads it as a release artifact.
build_androidApk:
needs: [flutter_test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.0'
- name: Build Android APK
run: flutter build apk --release
This runs only after tests pass.
For Android, after setting up the Flutter environment, the workflow calls flutter build apk --release
. This command compiles and packages the Android app into an .apk
file ready for distribution. The resulting file is placed inside the build/app/outputs/flutter-apk
directory of the project.
Here are the steps it follows:
Checks out repo, sets up Java, and sets up Flutter.
Runs:
flutter pub get
flutter clean
flutter build apk --debug
creates a debug APK.
Uploads APK using
ncipollo/release-action@v1
:artifacts: "build/app/outputs/apk/debug/*" tag: v1.0.${{ github.run_number }} token: ${{ secrets.TOKEN }}
Uploads all debug APKs as release artifacts.
Tags release as
v1.0.<run_number>
(e.g.,v1.0.5
).Uses a GitHub Personal Access Token (
TOKEN
) stored in repo secrets.
Step 4: Generate and Add a GitHub Token
The Android build job releases APKs using the release-action
. To authenticate, you must provide a GitHub personal access token. To do this, go to GitHub Settings → Developer settings → Personal access tokens.
Generate a new token with repo
permissions and copy the token immediately. Then go to your repository → Settings → Secrets → New repository secret. Add the token with the name TOKEN
.
Now the workflow can use ${{ secrets.TOKEN }}
securely.
Step 5: Understanding the Workflow
This workflow is triggered when code is pushed to the main
branch or when a pull request is opened against it. Let’s break it down:
Flutter Test Job
- Environment: Runs on
ubuntu-latest
.
Steps:
actions/checkout@v3
fetches the source code.actions/setup-java@v3
installs Java, required for some Flutter tools.subosito/flutter-action@v2
installs Flutter on the runner.flutter pub get
installs dependencies.flutter analyze
checks for code issues.flutter test
runs test cases.
This job ensures your code compiles, passes linting, and has no failing tests.
iOS App Build Job
Environment: Runs on
macos-latest
because iOS builds require macOS.Dependencies: This job runs only if
flutter_test
passes (needs: [flutter_test]
).
Steps: Similar setup as before, but after cleaning old builds with flutter clean
, it runs flutter build ios --no-codesign
to build an iOS app without requiring a signing certificate. The shell commands package the app into an .ipa
file.
Android APK Build Job
Environment: Runs on
ubuntu-latest
.Dependencies: Also depends on
flutter_test
.
Steps:
Installs Flutter.
Runs
flutter clean
and then builds the Android APK.Uses
ncipollo/release-action@v1
to upload the APK as a GitHub release, tagged automatically with a version likev1.0.<run_number>
.
Step 6: Push and Enable the Workflow
Save your file as .github/workflows/ci.yml
and push the changes:
git add .
git commit -m "Add GitHub Actions workflow"
git push
When you push your changes to GitHub, the workflow file is picked up automatically. To confirm that it is running, open your repository on GitHub and click on the Actions tab at the top of the page. You will see a list of workflow runs, each tied to the commit message that triggered them.
Click on the most recent run to expand the details. Inside, you’ll find separate jobs for Android and iOS builds. Each job will show its status in real time:
A yellow dot with “In progress” indicates the job is still running.
A green check mark with “Success” means the job finished successfully.
A red cross with “Failed” means something went wrong.
This way, you can immediately tell whether your Android and iOS builds passed or if one of them needs attention.
Final Notes
With this setup, you now have:
Automated testing whenever you push or open a pull request.
Automatic iOS builds on macOS runners.
Automatic Android builds with APKs released to GitHub.
This ensures that every change is tested and that builds are consistently generated without manual steps.
For more details, see the official GitHub Actions documentation: https://docs.github.com/en/actions.
Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & MoreÂ