Emilio Schepis

Adding continuous integration to a Flutter project with GitHub Actions

Introduction

Whenever you start a new project, either by yourself or as part of a team, it is important to define a set of rules to adhere to and to select tools that can automate the process of detecting if and when those rules are broken.

One such tool is the recently-announced GitHub Actions.

In this post I will show you how to setup a workflow to analyze, lint and test your Flutter project.

Getting started

A workflow is a set of steps that are executed whenever an event happens on your repository. Common triggers for these actions are pushes or pull requests on a given branch.

Workflows must be stored in the .github/workflows/ directory in your project. A single workflow is defined by a YAML file that can be named however we want.

For more information about Actions I highly recommend going through the extensive documentation.

A really nice feature of GitHub Actions is its generous free tier: you can run actions on Linux machines for up to 2000 minutes / month for free. If your project is open source (on a public repo), Actions are always free.

Setting up a CI workflow for Flutter

Step 1 - Setting up Pedantic on your project (Optional)

Pedantic is a Dart package created by Google that provides the extensive set of strict rules that Google itself uses in their projects.

You can add it as a development dependency to your pubspec.yaml file.

1dev_dependencies:
2  pedantic: ^1.9.0 # check the latest version on the package's page

We can then define the set of rules that Pedantic applies adding a analysis_options.yaml to the root of our project. To use Google's rules without overriding anything, the file is as straightforward as one line. Since we will be analyzing the project in our CI environment, we will add three more lines.

1include: package:pedantic/analysis_options.1.9.0.yaml
2
3analyzer:
4  exclude:
5    - flutter/** # Do not analyze the Flutter repository in the CI environment

This is needed because the GitHub Action will download a copy of the Flutter framework in the same directory of our project, making the Analysis step check the whole Flutter project, dramatically increasing the action duration.

Step 2 - Setting up the CI workflow file

Create a new workflow file in the workflows directory. For example you might create .github/workflows/ci.yaml.

1name: CI
2
3on:
4  push:
5    branches: [ main ]
6
7jobs:
8  workflow:
9    runs-on: ubuntu-latest
10    steps:
11      - uses: actions/checkout@v2 # (1)
12      - name: Setup Flutter # (2)
13        run: |
14          git clone https://github.com/flutter/flutter.git --depth 1
15          echo "$GITHUB_WORKSPACE/flutter/bin" >> $GITHUB_PATH
16      - name: Run Checks # (3)
17        run: |
18          flutter pub get
19          flutter format lib/** --set-exit-if-changed
20          flutter analyze --no-pub
21          flutter test --no-pub

Let's go through this file point by point:

  1. This Action, provided by GitHub, clones our project on the workflow machine

  2. This step clones the latest commit of the Flutter framework on the workflow machine and sets the PATH so that we can use flutter commands

    • If you added Pedantic in Step 1, this is why we needed to exclude the flutter directory from the analysis
  3. This step runs the actual checks on your code

    • Downloads all of your project's dependencies
    • Uses dartfmt to check your code's formatting (failing if it not properly formatted)
    • Analyzes it with Pedantic (highlighting broken rules)
    • Runs all tests (both unit tests and headless widget tests)

Result

Every push on the main branch will now trigger this CI action, providing quick and precise feedback on possible problems in the codebase. You can enhance this simple workflow with anything you might want to track (adding code coverage, build steps for each platform, and so on).

Conclusion

Having an automated process that checks your project's health at every push can be invaluable, especially when paired with extensive tests.

I really hope this will prove useful in your Flutter project.

Thank you for reading!