Your One-Stop-Shop for Flutter CI on GitHub
In today's world of software development, Continuous Integration (CI) is an essential part of the development process for most fancy-pants software development teams. However, many small Flutter teams still don't enjoy even the benefits of a CI setup. This article gives a great starting point!
๐ก you will know how to create a basic CI setup
๐ฐ you can add coverage badges and reports to your repo automatically
In today's world of software development, Continuous Integration (CI) is an essential part of the development process for most fancy-pants software development teams. However, many hobbyists and small Flutter shops still don't enjoy even the most basic benefits of a CI setup, maybe because they feel overwhelmed, or don't really know where to start. If you don't use CI on even your most niche side projects, I promise you, there is a better way. We'll provide you with a CI starter kit (batteries included) and introduce you to a free tool we built called Dart Coverage Assistant that you will (learn to) love! Keep reading, and within 10 minutes, you can have a GitHub workflow running that
- ๐งช Tests your code
- ๐ Checks your formatting
- ๐ Reports code coverage on your Pull Requests
- ๐ฐ Creates coverage badges for your READMEs
- ๐ด Improves your sleep through peace of mind (citation needed)
If all of this sounds attractive to you, let's dive in!
Flutter CI Starter Pack
If you are starting from zero, there are a few things your CI workflow should do for you. I know you're smart, but why not let the computer do some of the dumb stuff for you, so you can build and ship more?
The most basic CI workflow should accomplish a few things for you:
- Check your code for compile-time errors
- Run your unit tests (even if you don't have any yet)
For a Flutter Repository, this can quickly be achieved by adding the following file:
This should be good-to-go out of the box for most repositories, but a few things are notable. If you take a look at the on
section, the workflow will run on every Pull Request, and every push to the main branch. Using the Pull Request runs is a great way to make sure everything still works before you push to main
. And the concurrency
section will make sure that subsequent pushes cancel ongoing runs and save you a bunch of action minutes.
The next building block - Fancy Coverage Badges
After using your CI for a while, you might desire a bit more, and maybe you want to show off your progress and have some fancy-looking badges in your PRs and READMEs, to show off your 2% test coverage (๐ฅฒ). We built a GitHub Action called Dart Coverage Assistant, that adds coverage reports to your repository without any extra setup.
Our recommended Level 2 CI workflow looks as follows:
name: Continuous Integration
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
flutter-check:
name: Build Check
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pull-requests: write
steps:
- name: ๐ Checkout
uses: actions/checkout@v4
- name: ๐ฆ Setup Flutter
uses: subosito/flutter-action@v2
with:
sdk: 'stable'
- name: ๐ Check for compile-time errors and warnings
run: dart analyze . --fatal-infos
- name: ๐งช Run Tests
run: flutter test --coverage
- name: ๐ Generate Coverage
id: coverage-report
uses: whynotmake-it/dart-coverage-assistant@v1
with:
generate_badges: pr
As you can see, not that much has actually changed from the Level 1 workflow. The main two differences, are that the third step in the workflow now also fails when any analyser infos have not been dealt with, and the aforementioned dart-coverage-assistant action has been added. This will
- comment with a coverage report on every PR you open (it will even show you the difference in coverage that that PR makes)
- open a PR with svg coverage badges every time your coverage changes on
main
Dart Coverage Assistant also seamlessly handles monorepos, so if you manage multiple Dart projects from one repository, you won't even have to set anything up for that to work, as long as you ran your tests with --coverage
in all of the projects. And if you want to enforce a certain minimum coverage, or disallow PRs that decrease the coverage, that's possible too.
Bonus Level โ Check Code Generation
Many Dart and Flutter projects rely on Code Generation through packages like freezed, json_annotation, and many more. This houses the danger that you push a change to a class, but forget to run the code generation, so it's associated generated files weren't updated. In some cases, this will be detected already, since it can cause compile-time errors, however, it makes a lot of sense to check it separately.
Thus, if your project uses code generation, add the following code under the Level 2 workflow:
check_generation:
name: Check Code Generation
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: ๐ Checkout
uses: actions/checkout@v4
- name: ๐ฏ Setup Dart
uses: dart-lang/setup-dart@v1.6.4
with:
sdk: "stable"
- name: ๐จ Generate
run: dart run build_runner build --delete-conflicting-outputs
- name: ๐ Check there are no uncommitted changes
run: git diff --exit-code
This job will now fail if you committed something where the generated files are in a different state than they should be.