Run a Flutter App on an Emulator inside Docker
When building Continuous Integration (CI), you might find yourself needing to run your application on an emulator in use cases such as automated UI testing or generating screenshots. In this blog post, I’ll walk you through how to easily dockerize your Flutter application and run it on a dockerized Android emulator.
We’ll start by creating a new Flutter project, so make sure you have the Flutter environment set up with the latest version of Flutter. You can check your setup by running flutter doctor
, which gives you information about which version of Flutter is installed and how to update it if it isn’t the latest.
Go to your terminal at any desired location and run flutter create mydockerisedapp
to create a new Flutter application. You can also use File > New > New Flutter Project… in Android Studio, or use Visual Studio Code to create a new Flutter project.
This setup will require two Docker containers: one to run the application, and another for the Android emulator.
Flutter Docker Image
First, set up the Flutter application Dockerfile
, which contains the setup instructions for our Flutter Docker image:
FROM ubuntu:20.04 ENV DEBIAN_FRONTEND="noninteractive" ENV JAVA_VERSION="11" ENV ANDROID_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip" ENV ANDROID_VERSION="29" ENV ANDROID_BUILD_TOOLS_VERSION="29.0.3" ENV ANDROID_ARCHITECTURE="x86_64" ENV ANDROID_SDK_ROOT="/usr/local/android-sdk" ENV FLUTTER_CHANNEL="stable" ENV FLUTTER_VERSION="3.0.2" ENV GRADLE_VERSION="7.2" ENV GRADLE_USER_HOME="/opt/gradle" ENV GRADLE_URL="https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" ENV FLUTTER_URL="https://storage.googleapis.com/flutter_infra_release/releases/$FLUTTER_CHANNEL/linux/flutter_linux_$FLUTTER_VERSION-$FLUTTER_CHANNEL.tar.xz" ENV FLUTTER_ROOT="/opt/flutter" ENV PATH="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/emulator:$ANDROID_SDK_ROOT/platform-tools:$ANDROID_SDK_ROOT/platforms:$FLUTTER_ROOT/bin:$GRADLE_USER_HOME/bin:$PATH" # Install the necessary dependencies. RUN apt-get update \ && apt-get install --yes --no-install-recommends \ openjdk-$JAVA_VERSION-jdk \ curl \ unzip \ sed \ git \ bash \ xz-utils \ libglvnd0 \ ssh \ xauth \ x11-xserver-utils \ libpulse0 \ libxcomposite1 \ libgl1-mesa-glx \ && rm -rf /var/lib/{apt,dpkg,cache,log} # Install Gradle. RUN curl -L $GRADLE_URL -o gradle-$GRADLE_VERSION-bin.zip \ && apt-get install -y unzip \ && unzip gradle-$GRADLE_VERSION-bin.zip \ && mv gradle-$GRADLE_VERSION $GRADLE_USER_HOME \ && rm gradle-$GRADLE_VERSION-bin.zip # Install the Android SDK. RUN mkdir /root/.android \ && touch /root/.android/repositories.cfg \ && mkdir -p $ANDROID_SDK_ROOT \ && curl -o android_tools.zip $ANDROID_TOOLS_URL \ && unzip -qq -d "$ANDROID_SDK_ROOT" android_tools.zip \ && rm android_tools.zip \ && mv $ANDROID_SDK_ROOT/cmdline-tools $ANDROID_SDK_ROOT/latest \ && mkdir -p $ANDROID_SDK_ROOT/cmdline-tools \ && mv $ANDROID_SDK_ROOT/latest $ANDROID_SDK_ROOT/cmdline-tools/latest \ && yes "y" | sdkmanager "build-tools;$ANDROID_BUILD_TOOLS_VERSION" \ && yes "y" | sdkmanager "platforms;android-$ANDROID_VERSION" \ && yes "y" | sdkmanager "platform-tools" # Install Flutter. RUN curl -o flutter.tar.xz $FLUTTER_URL \ && mkdir -p $FLUTTER_ROOT \ && tar xf flutter.tar.xz -C /opt/ \ && rm flutter.tar.xz \ && git config --global --add safe.directory /opt/flutter \ && flutter config --no-analytics \ && flutter precache \ && yes "y" | flutter doctor --android-licenses \ && flutter doctor \ && flutter update-packages
Now, your Dockerfile
is set up.
Emulator
For the emulator, we’ll use an open source emulator image provided by Google. This will run as a separate Docker container, and we’ll connect to it from our Flutter application over a Docker network using adb
.
ℹ️ Note: This setup requires KVM, which is only available on Linux (Ubuntu and Debian), and therefore won’t work on Mac and Windows machines.
Docker Compose
Next, we’ll set up a docker-compose.yml
file to run the containers together:
version: '3.6' services: app: build: context: '.' dockerfile: Dockerfile image: 'flutter' working_dir: 'app' depends_on: - emulator volumes: - './:./app' emulator: image: us-docker.pkg.dev/android-emulator-268719/images/30-google-x64:30.1.2 expose: - 8554/tcp - 5555/tcp devices: - '/dev/kvm:/dev/kvm' env_files: - .env
As you might notice, the Flutter application app
depends on the emulator
service, and they’re linked by a Docker network. There’s no need to define any networks, because Docker Compose will automatically create a default network.
Bash Script
Lastly, we’ll set up the run.sh
script with the commands needed to run the application:
#!/bin/bash # Connect to the Android emulator container. adb connect emulator:5555 #List the available Flutter devices. You should see the connected dockerized emulator. flutter devices # Install the Flutter packages. flutter pub get # Run the Flutter app on the connected emulator. # Add `--trace-startup` to stop the Flutter process from blocking the terminal. flutter run --trace-startup mkdir -p 'screenshots' || exit 1 # Take a screenshot of the Flutter app. adb shell screencap /sdcard/screenshot.png adb pull /sdcard/screenshot.png screenshots/flutter-screen.png
Now, add execute permissions to run.sh
by running chmod +x run.sh
Running the Application
Finally, run the application as shown in the script below:
$ docker compose run app run.sh
This will set up the Docker containers and execute run.sh
inside the app
container. Wait for the execution to complete, and you’ll see the flutter-screen.png
image file in the screenshots folder.
Conclusion
In this blog post, we looked at how to run a dockerized Flutter application on a dockerized Android emulator. The setup can be used for purposes such as automated UI testing and automatically generating screenshots.
FAQ
How do you set up the Docker environment for a Flutter app?
You set up the Docker environment by creating a Dockerfile for the Flutter app, an emulator image, and a docker-compose.yml
file to manage the services. The Dockerfile sets up the necessary dependencies and configurations, while the docker-compose.yml
file defines the services and their relationships.
What is the role of the run.sh
script in this setup?
The run.sh
script contains the commands to connect to the Android emulator, list available Flutter devices, install Flutter packages, run the Flutter app, and take screenshots. It is executed inside the Docker container to automate these tasks.
Can this setup be used on Windows or Mac machines?
No, this setup requires KVM, which is only available on Linux (Ubuntu and Debian). It will not work on Mac or Windows machines.
How do you run the Flutter app on the dockerized emulator?
After setting up the Docker containers, you can run the Flutter app by executing the run.sh
script inside the app
container using the command docker compose run app run.sh
. This will build and run the app on the connected Android emulator.
How do you generate a screenshot of the Flutter app in this setup?
The run.sh
script includes commands to take a screenshot of the Flutter app running on the emulator. It uses adb shell screencap
to capture the screen and adb pull
to retrieve the screenshot from the emulator to the local system.
Julius joined Nutrient in 2021 as an Android engineer and is now the Cross-Platform Team Lead. He has a passion for exploring new technologies and solving everyday problems. His daily tools are Dart and Flutter, which he finds fascinating. Outside of work, he enjoys movies, running, and weightlifting.