<- all articles

Flutter Web on Google App Engine using Cloud Build

In this blog, we will explain the deployment process to Google App Engine for a Flutter Web application, orchestrated by Google Cloud Build.

Why are we writing this post? 🧐

We love Flutter at Craftworkz. Period.

It’s beautifully powerful, developer-friendly, and has recently shed its mobile-oriented shell by going full cross-platform with the launch of Flutter 2 (and Flutter 2.2 has just been announced at Google I/O).

We have already built a lot of web apps using a wide range of developer frameworks, most notably React and Angular. The question now asks itself: “Why not fully switch to Flutter as we love it so much when building mobile apps and Flutter Web is now in GA?”

To be ready for production usage by our clients, an important requirement must be met by Flutter Web. A fully automated CI/CD pipeline needs to be able to build and deploy our application upon each commit on the main branch of our repository. Essentially, manual deploys are out of the question.

So, what will we discuss in this post?

  • Setting up an App Engine service, hosting your Flutter Web application
  • How to enable Cloud Build to automatically keep your deployment in sync with your codebase
  • General thoughts on the whole process

The source code and demo setup are also available as a public Github repository here. Feel free to suggest updates there in the future or below this post. Let’s dive in!

Setting up your Google Cloud project 🤘🏼

Okay, let’s start at the beginning. First, you’ll need a Google Cloud project to be able to deploy your application. Duh.

If you have trouble with any of the following steps, you can find answers to most of your questions on Google’s Documentation pages and configure your project using the GCP Console.

  1. Create a Google Cloud project with a descriptive name. We created a demo project with the name craftworkz-blog-flutter-demo
  2. Enable billing for your project.
    Hosting the Flutter Web application will most likely be free as Google’s free tier is quite large, but you’ll need the billing account to be able to use the components used in this post.

    And if you’re a new GCP user, you get €300 in free credits.
  3. Setup the Google Cloud SDK for your machine.

gcloud initgcloud config set project MY_PROJECTgcloud config set region europe-west1

If this setup was successful, it’s a good idea to enable some APIs for future usage.

gcloud services enable cloudbuild.googleapis.comgcloud services enable appengine.googleapis.com

Testing your Flutter Web application locally 👀

We assume you have created a Flutter Web application locally that works when running it locally using flutter run -d chrome

We noticed that sometimes, the local Flutter runner has some extra tricks up its sleeve to keep your application running smoothly while the production build will not work as expected. For example, when loading an Image asset using the Image.network(...) function, the Flutter runtime locally doesn’t throw an error, but when hosting the built version, it does throw a 404.

There is a simple solution to test if your application has this issue. Simply run and test the built version locally first:

flutter build webcd build/web python3 -m http.server 8000

If everything runs as expected, we’re good to go! If not, you might want to check your web console output (F12 for dev tools) or go back to the Flutter runner output to see if you have any remaining rendering issues.

Be sure to test a few different desktop and mobile screen sizes and orientations before deploying your current version. We have experienced some issues during development due to our code not being responsive enough to cope with small mobile screens (there are still lots of people using iPhone SE-type screens out there, and these are smaller than you think).

Simple App Engine deployment setup 🦾

First, let’s make sure we can deploy to App Engine and have a running version of our Flutter Web application on App Engine deployed from our local machine before we set up the full Cloud Build pipeline.

First, enable App Engine and create an application in a region of your choice, if you haven’t done so already using the web console:

gcloud app create --region=europe-west

Then add an app.yaml file to your project that describes your App Engine application and redirects all traffic to HTTPS and your build/web folder:

env: standardruntime: nodejs14handlers:  - url: /    static_files: build/web/index.html    upload: build/web/index.html    secure: always    redirect_http_response_code: 301  - url: /(.*)    static_files: build/web/\1    upload: build/web/(.*)    secure: always    redirect_http_response_code: 301

This works fine but is not as clean as we might want, because we’re uploading our source files to App Engine, while we only need the build/web static contents.

So, if you do want to do it cleanly, use the following app.yaml configuration and add the file to the build/web dir before you push to App Engine:

env: standardruntime: nodejs14handlers:  - url: /    static_files: index.html    upload: index.html    secure: always    redirect_http_response_code: 301  - url: /(.*)    static_files: \1    upload: (.*)    secure: always    redirect_http_response_code: 301

Remember: don’t add it to the build/web directory in your source code, as this directory should not be committed to Git, and files are overwritten at build time. Add the app.yaml to your project root and copy it to the build/web directory before deploying to App Engine.

Let’s push our app to App Engine! (remember to build your code first by running flutter build web before attempting a deploy).

Using the clean method, first push the app.yaml file to the build dir and deploy only the built assets:

cp app.yaml build/webcd build/webgcloud app deploy

... or when using the working, but less clean, first app.yaml config approach, you can just deploy from the project root:

gcloud app deploy

If everything went correctly, your application should now be up, running, and available at the URL provided by the console output. For our demo project, you can check it out here: https://craftworkz-blog-flutter-demo.ew.r.appspot.com, if you want to.

Let’s automate this deployment using Cloud Build ☁️

Cloud Build is another one of Google’s tooling that we just love. Any pipeline can be configured using the console or by adding a cloudbuild.yaml file in your repo. Make sure to give Cloud Build enough IAM rights to access all needed services by going to the GCP console Cloud Build settings and enabling the App Engine Admin and Service Account User roles.

Settings Flutter - Cloud build
Enable the App Engine Admin and Service Account User roles.

First make sure your source code is available on Cloud Source Repositories, the git repository tool provided by GCP. You can do this in 2 ways:

  1. Use Source Repositories as your main and only git repository by creating it there;
  2. Link Source Repositories to your repo, hosted by a third party like Bitbucket or Gitlab. You can also reference our public Github repository containing all the needed code.
Cloud source repositories Flutter
Connecting your GitHub repository to Google Cloud Source Repositories is really easy.

Secondly, we need a Cloud Build runner that can build our Flutter code for the web. At the time of writing this, there is no standard Cloud builder image available for Flutter (see list here). But here’s where Cloud build shines because there is a whole list of community-maintained builders available that you can build yourself, to use in your pipelines. And yes, Flutter is supported!

To build your Flutter builder image, follow these steps (code below list):

  1. Clone the community builders repository to your machine
  2. Navigate to the Flutter folder
  3. Build all versions of the Flutter builder using Cloud Build. These will be available within your project’s Container Registry

git clone https://github.com/GoogleCloudPlatform/cloud-builders-communitycd cloud-builders-community/fluttergcloud builds submit --config cloudbuild.yaml .

If you are not planning to do an Android build with this image in the future, or when you are running into Android license errors during this build, you can change the provided Dockerfile in the cloud-builders-community/flutter folder:

FROM debian:stretch# Install Dependencies.RUN apt update -yRUN apt install -y \  git \  wget \  curl \  unzip \  lib32stdc++6 \  libglu1-mesa \  default-jdk-headless# Install Flutter.ENV FLUTTER_ROOT="/opt/flutter"RUN git clone https://github.com/flutter/flutter "${FLUTTER_ROOT}"ENV PATH="${FLUTTER_ROOT}/bin:${PATH}"# Disable analytics and crash reporting on the builder.RUN flutter config --no-analytics# Perform an artifact precache so that no extra assets need to be downloaded on demand.RUN flutter precache# Perform a doctor run.RUN flutter doctor -v# Switch to the correct channelARG channel=stableRUN flutter channel $channel# Perform a flutter upgradeRUN flutter upgradeENTRYPOINT [ "flutter" ]

This configuration is available in our Github repository as well, so you can simply run:

# from repository rootcd cloud-builder-fluttergcloud builds submit --config cloudbuild.yaml .

After this build has been completed, your GCP projects Container Registry will contain a Flutter builder image at gcr.io/$PROJECT_ID/flutter:stable, usable in the Cloud Build pipeline. You can confirm this by going to your projects Container Registry page at https://console.cloud.google.com/gcr/images/$YOUR_PROJECT_ID.
Isn’t that just amazing?

Now, let’s add the cloudbuild.yaml which will define our build steps for Cloud Build (we assume you have the second app.yaml file present in the root directory of your project):

steps:  - name: 'gcr.io/$PROJECT_ID/flutter:stable'    args: ['build', 'web']  - name: gcr.io/google.com/cloudsdktool/cloud-sdk    args:      - '-c'      - cp app.yaml build/web && cd build/web && gcloud config set app/cloud_build_timeout 1600 && gcloud app deploy    entrypoint: bashtimeout: 1600s

When all these files have been committed and pushed to Source Repositories or your connected repository, you can create the build trigger in the Cloud Build dashboard:

Cloud build dashboard
Sample Cloud Build trigger configuration

Et voilà, your setup should now be complete! Upon each push to the main branch, your app should be automatically rebuilt and deployed. 💪

Is something not working as expected, or do you just want to check out a demo repository containing the configuration described in this post? Go check out the Github repository containing everything discussed in this post!

Want to see a Flutter web page in action using this pipeline? Check out our event page for Raccoons Reimagines or the sample demo page built for this blogpost.

Are you interested in building your Flutter-based web application and looking for technical and creative support? Contact us at info@craftworkz.be or fill out the contact form on our website.

Written by

Want to know more?

Related articles