Serve A Flutter Web App From A Docker Container

December 1, 2020

What is Flutter?

Flutter is a UI toolkit for building natively compiled applications for mobile, web, and desktop platforms with a single codebase. It represents the most recent effort towards what Java promised: a “Write Once, Run Anywhere” experience. Writing a single codebase that runs on many platforms offers a number of advantages. Traditionally, if an organization wanted to support their application on web, mobile, and desktop it would have to support 3 separate codebases all in different languages and using different technologies. Paying for 3 different engineering teams to support one product is an expensive proposition. Java was one of the first technologies that attempted to solve this problem but it was stymied by a number of challenges. These included a lack of support from certain major corporations, performance issues, and security issues. Luckily, Google has offered us Flutter as a promising candidate to bring about the reality of a truly crossplatform development experience.

Flutter for Web

Flutter’s support for the Web is still in the Beta stage of development. However, a number of organizations are already using it successfully for production sites. From an experience and functionality point of view, developing a Flutter app for the web is almost exactly the same as for any of the other platforms. The main thing that is different is how the app is served and deployed. Because Flutter for web is so new, users are discovering new ways of doing things all the time. In my own development, I am building an app designed around microservices that will eventually be deployed with Kubernetes. I wanted to serve my front end Flutter app just like any other front end from within a container but I couldn’t find any direct literature on doing this. After figuring out how to do it on my own I wanted to share it with the world, which is the purpose of this article.

I am fairly sure that someone who stumbles across this article will have been looking for it. Therefore, I assume basic knowledge of Docker, Flutter, and web development basics. However, in the spirit of helpfulness I will include links to important materials. I assume the reader is using Linux but the information is easily adapted to other platforms. As a general tip for reading new technical guides I always recommend that the reader reads the entire guide before following any setup steps.

Setup Flutter for Web Locally

  1. Install Flutter for your platform
  2. Enable Flutter Web Support
  3. Install VSCode Flutter Extensions
  4. Install Docker
  5. Install the VS Code Docker Extension
  6. Create a new Flutter Project
    1. flutter create flutter_web_docker_example
  7. Test that you can run your Flutter web app locally by:
    1. cd into the project directory
    2. Run flutter run -d chrome

How is Flutter for Web Deployed?

A Flutter app on the web is eventually translated down to HTML and Javascript. Let’s convince ourselves of this by building our Flutter web app with flutter build web while in the project directory. After this is done you will see a new folder in the build directory in the project called web:

FLUTTER WEB APP DIR PICTURE

As you can see this looks sort of like a release folder for a standard web site. This entire web directory needs to be served together. Make sure to inspect the index.html file and notice that it specifies the script source to be main.dart.js. This file is the compiled Flutter app. This is incredibly neat because a user could actually embed the Flutter app anywhere in any standard html page and serve it how they like. Although I haven’t explored it further, I am sure some pretty cool stuff could be done using this technique.

At this point, we have convinced ourselves that all of the magic has been taken care of by Flutter and that all we need to do to serve the app is serve this build directory.

Set up a Docker Container to Serve the App

We will use a Dockerfile to specify our container. This Dockerfile will:

  1. Install Dependencies - The Debian image will be set up and relevant packages installed
  2. Set up Flutter - Flutter will be installed and set up in the container
  3. Copy the app to the container - The local app will be copied to the container in order to be built
  4. Build the app - Once the files are copied to the container the Dockerfile will build the app
  5. Expose a port - Our Flutter app will be served on port 4040
  6. Configure and start the server in the container - Our Dockerfile will start a local http server in the container. This server will be started using a script that we will add to our project

Let’s look at the Dockerfile now. I’ve clearly labeled what each section of commands does.

# Install dependencies
FROM debian:latest AS build-env
RUN apt-get update 
RUN apt-get install -y curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-mesa fonts-droid-fallback lib32stdc++6 python3 psmisc
RUN apt-get clean

# Clone the flutter repo
RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter

# Set flutter path
ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}"

# Enable flutter web
RUN flutter channel master
RUN flutter upgrade
RUN flutter config --enable-web

# Run flutter doctor
RUN flutter doctor -v

# Copy the app files to the container
COPY . /usr/local/bin/app

# Set the working directory to the app files within the container
WORKDIR /usr/local/bin/app

# Get App Dependencies
RUN flutter pub get

# Build the app for the web
RUN flutter build web

# Document the exposed port
EXPOSE 4040

# Set the server startup script as executable
RUN ["chmod", "+x", "/usr/local/bin/app/server/server.sh"]

# Start the web server
ENTRYPOINT [ "/usr/local/bin/app/server/server.sh" ]

Before we start the Dockerfile we need to add the server script file to the project. It will be copied to the container with the rest of the app files. I am using a Python HTTP server for my development but any HTTP server can be used as long as it is installed and configured in the Dockerfile and is able to be started in this script. Add a folder called server and a file inside it server.sh to the app directory:

FLUTTER_DOCKER_SERVER_PATH IMAGE

Add these contents to the server.sh file:

#!/bin/bash

# Welcome
echo 'Server start script initialized...'

# Set the port
PORT=4040

# Kill anything that is already running on that port
echo 'Cleaning port' $PORT '...'
fuser -k 4040/tcp

# Change directories to the release folder
cd build/web/

# Start the server
echo 'Starting server on port' $PORT '...'
python3 -m http.server $PORT

# Exit
echo 'Server exited...'

fuser is a Linux command that is used to kill processes on a port. There are similar commands in Windows so if you decide to run this file locally you should implement the equivalent Windows command. Notice that we start on port 4040 which is the same one that is exposed in the Dockerfile. This port can be changed as you need it as long as it is changed in the Dockerfile as well.

Build and Run the Docker Image

Now it is time to build and run the container to see the app being served from within it.

Build the Container

  1. Change Directories into the root project folder
  2. Run the command docker build . -t flutter_web_docker_example

This command will build a Docker image specified by the Dockerfile above and name it ‘flutter_web_docker_example’.

Run the Container

Let’s run the container and see our app being served!

  1. Change Directories into the root project folder
  2. Run the command docker run -i -p 808:4040 -td flutter_web_docker_example
  3. You should see a hash of the container

This docker run command binds the container port 4040 to the TCP port 808. See this link for more information on docker ports.

Now if you go to the Docker view in VSCode you should see our container running:

FLUTTER WEB CONTAINER RUNNING IMAGEs

The app is now running on localhost:808 . Navigate there in a browser to see the app!

FLUTTER WEB APP RUNNING

Conclusion

This article demonstrated how to serve a Flutter web app from a container. This technique can be used to deploy Flutter web apps in the could using container hosting services. This technique lends itself to microservice architectures and offers the ability to make smooth and homogeneous deployments.

Nifty tech tag lists fromĀ Wouter Beeftink