Flutter Web CI/CD with Google Cloud

With the recent release of Flutter 2, web is now a stable target for Flutter apps opening a new door for developers to create beautiful websites. This will be a basic guide to automatic deployment of Flutter websites and web apps using GCP Cloud Build, Storage, and Cloudflare.

By following this guide, your app will be automatically published when you git push to your repository’s master branch. This simplifies deployment, and even pushes Flutter to be viable as a static site generator (in the same vein as Hugo). As an example, I will be deploying the placeholder website shown below. When I later update it with additional content and, the build pipeline will automatically publish it to the web for me.

Screenshot of sample placeholder website built using Flutter.

This guide will assume you are familiar with git, have access to a domain and its DNS configuration, and already have a Flutter web app ready to deploy. If you don’t have one yet, check out this official guide on Building a web application with Flutter. You should be able to run flutter build web to generate static files in the build/web directory.

You should also have a Google Cloud Platform account already set up, and have access to Cloud Source Repositories, Cloud Build, and Storage. You also may want a Cloudflare account to manage your DNS and enable HTTPS with Storage.

You will need to get your code into GCP Source Repositories, by clicking “Add Repository”. If you already use Github or Bitbucket you can simply mirror your existing repo, otherwise you can create a new one. I’d recommend mirroring as it simplifies authentication.

Importing a Github repository into Source Repository.

Push your Flutter project source code if necessary, and once you see it showing up in Source Repository you’re ready to move on to the next step.

I will be using Cloudflare to configure my DNS, but you can use whichever provider you chose. The advantage of using Cloudflare is that they also also provide a free reverse proxy, which enables an HTTPS interface to GCP Storage. This provides some security against MITM attacks (for example on public wifi) and gives you the nice padlock icon in web browsers, without taking too much effort.

Whichever provider you use, you will first need to verify your domain ownership with GCP, which involves adding a TXT record. Next, you will need to add a CNAME record pointing from @or a subdomain to c.storage.googleapis.com . Optionally, add a CNAME alias for the www subdomain. The configuration should look as below (replacing sayes.systems with your [sub]domain).

Configuring the domain’s DNS in Cloudflare to point to GCP Storage.

(Optional) To enable HTTPS in Cloudflare, navigate to the SSL/TLS→Overview page, and select the “Flexible” encryption mode. You may also want to enable “Always use HTTPS” on the SSL/TLS→Edge Certificates page.

Navigate to the GCP Storage Browser and click “Create Bucket”, then enter your domain (in my case, sayes.systems ) as the bucket name. Select a nearby region, and the defaults for other options. Ensure that you verified your domain in the previous step; this is necessary in order to create a domain-named bucket.

Back in the Storage Browser, open the context menu for your newly created bucket, and select “Edit Bucket Permissions”, then “Add Member” in the side panel. Under new members, type allUsers , and select the role Cloud Storage→Storage Object Viewer. This will make the content of your bucket publicly accessible. Ensure it looks as below, then hit “Save”.

Enabling public viewing of bucket.

Next, select “Edit website configuration” from the same context menu on your bucket. Enter index.html as the Index page suffix to match with the page filename that Flutter generates.

Navigate to the Triggers page in GCP Cloud Build, and click “Create Trigger”. Enter a name of your choice, and ensure to use the follow configuration:

  • Event: Push to branch
  • Source→Repository: the name of the repository you created in step one
  • Source→Branch: leave as ^master$ , or replace “master” with whatever branch you wish to trigger the deploy
  • Build configuration: Cloud Build configuration file, with default file location of /cloudbuild.yaml .
Important configuration for the Cloud Build trigger.

Finally, let’s make the cloudbuild.yaml file that we just set up to be triggered by Cloud Build. The goal of the pipeline is to first build the Flutter web app, and update our bucket with those files.

The docker image for building with Flutter from the cloud-builders-community repo is a little outdated and failed to build for me, but lucky for you readers I’ve made a custom flutter image optimised for web builds, available on Docker Hub. So without further ado, here is the contents of cloudbuild.yaml (remember to replace sayes.systems with your domain/bucket name):

steps:
- name: docker.io/elliotsayes/flutter-web:latest
args: ["build", "web"]
- name: gcr.io/cloud-builders/gsutil
args: [ "-m", "rsync", "-r", "-c", "-d", "build/web", "gs://sayes.systems" ]

Place the cloudbuild.yaml in the root of your flutter project repository, push to master, and hey presto! You should see a build popping up in your Cloud Build history. All going well it will complete successfully with a satisfying tick!

What victory looks like

At this point, you can check your bucket to make sure the files have made it inside. Go to http://storage.googleapis.com/sayes.systems/manifest.json (replacing sayes.systems with your domain), if you can see some json then the files are publicly visible. Finally, visit your domain itself to view the live website in action!

My live website! Animations built natively in Flutter.

Conclusion

I hope you found this article useful, and that it will inspire you to create your own Flutter web apps and build pipelines. If this helped you, please give it a clap, and if you have any questions, comments or found any mistakes please leave a reply. For more content like this follow me on medium or on twitter.

A discerning reader may also want to consider Firebase Hosting as an alternative for GCP Storage, or GCP Cloud Load Balancing as an alternative to Cloudflare HTTPS.