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’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.
flutter run -d chrome
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.
We will use a Dockerfile to specify our container. This Dockerfile will:
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.
Now it is time to build and run the container to see the app being served from within it.
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’.
Let’s run the container and see our app being served!
docker run -i -p 808:4040 -td flutter_web_docker_example
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
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.