Damilare Jolayemi Damilare is an enthusiastic problem-solver who enjoys building whatever works on the computer. He has a knack for slapping his keyboards till something works. When he's not talking to his laptop, you'll find him hopping on road trips and sharing moments with his friends, or watching shows on Netflix.

Containerizing Flutter web apps with Docker

5 min read 1486

Containerizing Flutter web apps with Docker

One of the hassles a lot of software engineers face revolves around deployment. An engineer could build and test an application with all dependencies on a Windows OS without errors. Still, deploying the same application to a different operating system, such as Linux or Mac, could result in bugs and errors.

Before container technologies emerged, virtual machines (VMs) were the go-to solutions for dealing with such encounters. It turns out that VMs are slow and memory inefficient, partly because they have to be set up and launched for each application.

The technique of containerizing applications complements the inefficiencies of VMs; containers are lightweight packages that enable an application to run consistently on any operating system or infrastructure.

This tutorial will dive further into what it means to containerize an application and its benefits. We will investigate this by setting up Docker on our machine and containerizing a Flutter web app.

Prerequisites

  • Dart, Android, and Flutter SDKs installed on your machine
  • Docker toolkit

I’ll be using Visual Studio Code for this demonstration. To efficiently run Docker on Windows, ensure that your machine:

  • Runs on Windows v10
  • Has at least 4GB of RAM

What are containers?

Container technologies make it possible to run applications in distributed environments consistently. These environments can include:

  • Transitioning through the developer’s laptop to the testing, staging, and production environments
  • Migrating from a physical machine to a virtual machine in the cloud

There is usually no guarantee that the application is exposed to consistent runtime protocols across these environments. For instance, you may build and test an application using Flutter v2.2.3, but at the production stage, the application ends up running on Flutter v2.5.1 and, as a result, something goes wrong.

Containers ensure that an application maintains its configured runtime protocol irrespective of the operating configurations of the host server.

It achieves this by packaging an application with its dependencies, settings, libraries, and other runtime protocols in an isolated state. This implies that the application runs independently of the hosting server configurations and without any incompatibilities.

What is Docker?

Docker is an open source tool for building, testing, deploying, and managing containerized applications. In my opinion, it is the standard for implementing application containerization due to its ease of portability and design for modern microservice architecture.

We made a custom demo for .
No really. Click here to check it out.

Docker simplifies the process of orchestrating containers through its docker commands.

Benefits of containerizing your app with Docker

Cost efficiency

A server hosting three applications in three virtual machines would need three copies of a virtual OS managed by the hypervisor to run on the server. This requires a lot of resources because VMs that run these three apps will be very heavy and will consume a lot of CPU resources.

With containers, the container engine manages all three applications and shares the same host OS. A single infrastructure can support a variable amount of containers.

Remote services

A developer can push and pull Docker images to and from a repository. Any machine can host these images to run its containers. This implies that you can always retrieve and run a container in your Docker image from wherever you are. This process is similar to how you would typically retrieve your application hosted on a remote Git repository.

Automatic setup

Docker abstracts the process of manually setting up the application dependencies each time you change the application environment. Due to the independence and isolation of the Docker container, you don’t need to set up your application runtime configuration. The container already contains the dependencies that your application needs to be self-sufficient.

Once you build the image, Docker uses the configurations provided to sustain the application.

Scalability

Scaling up an application is more manageable with containers when compared to virtual machines. Docker containers can scale up applications to withstand a heavier load. It can also reduce the processing effort when there is a drop in the weight of the load on the application.

Security

The configurations of containers are immutable. When making changes to the configuration of an image, the container is replaced wholly and the image is rebuilt.

Setting up Docker

Let us proceed to containerize a Flutter web app with Docker.

As I mentioned at the start of this article, I’ll demonstrate this process on a Windows operating system. Once you have installed Docker on your system, open the command shell as an administrator.

Run the following command to check the version of Docker installed:

sh
docker --version

You should get an output displaying the version of Docker that you installed, in this format:

sh
Docker version 20.10.8, build 3967b7d

Next, let us test the installation by running a basic Docker command on the terminal.

sh
docker run hello-world

The expected output for this command is:

The expected output of the Hello World test with Docker

When run for the first time, the above command pulls the image from the Docker hub and runs an instance of it as a container.

If you are using a Linux operating system, you would have to grant root access to Docker to use it in your IDE. You can do this by running the following commands:

Create the Docker group:

sh

sudo groupadd docker

Add your user to the group:

sh

sudo usermod -a -G $USER

Activate the changes made to groups:

sh

newgrp docker

Now, verify you can run Docker commands from the shell without using the sudo command.

sh
docker run hello-world

Creating the Docker container

Open the Flutter web application you’d like to containerize with your preferred IDE. Inside the root application folder, create a file named Dockerfile. Ensure the filename starts with uppercase “D”.

Now, add the following code inside the Dockerfile:

sh

# Install Operating system and dependencies
FROM ubuntu:20.04

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
RUN apt-get clean

# download Flutter SDK from Flutter Github repo
RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter

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

# Run flutter doctor
RUN flutter doctor

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

# Copy files to container and build
RUN mkdir /app/
COPY . /app/
WORKDIR /app/
RUN flutter build web

# Record the exposed port
EXPOSE 5000

# make server startup script executable and start the web server
RUN ["chmod", "+x", "/app/server/server.sh"]

ENTRYPOINT [ "/app/server/server.sh"]

In the above Dockerfile, we started by installing the operating system (Ubuntu) and the required dependencies.

Next, we downloaded the Flutter SDK and set the SDK path in the environment path (this is also what you would do if you were setting up Flutter on your local machine).

Afterward, we copied our application to the container and built it with the Dockerfile.

Finally, we exposed a port=5000, through which we’ll serve the application from the container. Then we start the HTTP server using the server.sh script.

Next, create a subfolder in the root application folder. Name the folder server, then create a server.sh file inside the server folder, and add the following code.

sh

#!/bin/bash

# Set the port
PORT=5000

# Stop any program currently running on the set port
echo 'preparing port' $PORT '...'
fuser -k 5000/tcp

# switch directories
cd build/web/

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

In the server.sh file, we set the port to 5000, the same as what we exposed in the Dockerfile.

Then, we use the fuser command to terminate any other process currently running on port 5000 to make it available for our application. Finally, we start the server.

Build the Docker image

Open the terminal from the application root folder and run the following command:

docker build . -t flutter_docker

This will build a Docker image with the name flutter_docker. You can view this image from the installed Docker desktop application. You can also view the image with the command docker images.
The Docker image file is visible on your disk view

Run the image container

Run the following command:

docker run -i -p 8080:5000 -td flutter_docker

This command binds the port 5000 configured in the container to the TCP port 8080, accessible from the browser.

Your project structure should be in this format by the time you have completed the configurations:
The expected project structure

Proceed to view the application on localhost:8080 on your browser.
You can now view the application via your local host environment

Conclusion

In this tutorial, we learned about the benefits of containerizing an application. We demonstrated how to set up Docker on our machine, and we proceeded to run our existing application from a Docker container.

With Docker, you don’t have to worry about your application breaking due to the incompatibilities of its host environments. You can learn more about Docker from the official documentation.

The aim is to ensure your application is flexible enough to run independently on its host. As we have demonstrated with a Flutter application, you can also containerize applications built with other tools.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Damilare Jolayemi Damilare is an enthusiastic problem-solver who enjoys building whatever works on the computer. He has a knack for slapping his keyboards till something works. When he's not talking to his laptop, you'll find him hopping on road trips and sharing moments with his friends, or watching shows on Netflix.

Leave a Reply