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.
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
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.
Step One: Source Control
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.
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.
Step Two: DNS and (Optional) HTTPS
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).
(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.
Step Three: Storage Bucket
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”.
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.
Step Four: Build Trigger
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
Step Five: Build Pipeline
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):
- 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" ]
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!
At this point, you can check your bucket to make sure the files have made it inside. Go to
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!
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.