TLDR :)
My first annoyance with NixOS
This all started when I tried to make an Android app using the Flutter framework. The first thought on my mind was to simply create a shell.nix
or a flake.nix
file to download flutter and androidSdk tools. I tried this with flakes as my system packages are NixOS stable packages and I didn’t feel like changing it. I was very displeased to find that when I had created my flake.nix
file (with only flutter and androidSdk from NixOS unstable channel, mind you), it started downloading like 40 GiB of data. Feel free to make fun of me, when there is something obvious that I am missing, but PLEASE reach out if you know the solution. 40 GiB might not sound like too much for some of you, but I have an astronomically slow internet connection and I am working on a laptop with about 250 GB of storage so after leaving it downloading overnight, I woke up to a failed build and a completely full disk.
If you have ample storage and a speedy internet connection, I urge you to use Nix for that. Just know that as of the time I am writing this, Android SDK is only available on NixOS unstable. Just because I didn’t get it working, I am not going to share the flake.nix
on here, but I believe you will be able to find a sample (that is also more up to date) on the internet. And just if you’re wondering: no, I was not interested in just using Android Studio to manage my SDK. My storage space is even now running low.
Devcontainers
As I am in a committed relationship with Docker (not sponsored, just love it), I turned to the possibility of using containers as build environments. This took me to the Devcontainer standard. I used a Dockerfile
from the tutorial on blog.devops.dev and changed it up a bit. That mainly means updating the version numbers in the Dockerfile
. It should be mentioned that you need Docker installed on your system and its permissions given to your user. The directory structure of the devcontainer configuration in your project root should look like this:
.devcontainer/
├─devcontainer.json
└─Dockerfile
Devcontainer configuration
This is actually quite simple If you know Docker. Even if you don’t, there is a VSCode extension that made creating devcontainers extremely easy. But I like this setup more, because It gives me every bit of control I need to debug the problems that inevitably happen while trying to build your project. I also forward the port 5555 from the container to my host to debug my app over adb. Here are my files:
devcontainer.json
{
"name": "Flutter",
"build": {
"dockerfile": "Dockerfile"
},
"forwardPorts": [
5555
],
"runArgs": [
"--privileged",
"-v",
"/dev/bus/usb:/dev/bus/usb"
],
"remoteUser": "root",
"customizations": {
"vscode": {
"extensions": [
"Dart-Code.flutter",
"Dart-Code.dart-code",
"alexcvzz.vscode-sqlite"
]
}
}
}
Dockerfile
FROM ubuntu:focal
# Set environment variables
ENV ANDROID_SDK_ROOT=/usr/lib/android-sdk
ENV FLUTTER_SDK_ROOT=/usr/lib/flutter
ENV FLUTTER_SDK_VERSION=3.29.2-stable
# Include flutter and android tools in path
ENV PATH="${PATH}:${FLUTTER_SDK_ROOT}/bin:${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:${ANDROID_SDK_ROOT}/platform-tools"
# Enable noninteractive installation
ENV DEBIAN_FRONTEND=noninteractive
# Install dependencies and toolsDockerfile2
RUN apt-get update && apt-get upgrade -y && \
apt-get install -y \
watchman \
openjdk-17-jdk \
android-sdk \
wget \
unzip \
zip \
curl \
git-all \
xz-utils \
libglu1-mesa \
libc6:amd64 libstdc++6:amd64 lib32z1 libbz2-1.0:amd64 && \
rm -rf /var/lib/apt/lists/*
# Add flutter to safe directory in git
RUN git config --global --add safe.directory /usr/lib/flutter
# Download and install Android Command Line Tools
RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \
wget -O commandlinetools.zip "https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip" && \
unzip commandlinetools.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest && \
rm commandlinetools.zip
# Accept Android SDK licenses and install necessary components
RUN yes | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --licenses && \
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-34" "build-tools;33.0.0"
# Insall Flutter SDK
RUN mkdir -p ${FLUTTER_SDK_ROOT} && \
cd ${FLUTTER_SDK_ROOT} && \
curl -OL https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_SDK_VERSION}.tar.xz && \
tar -xf flutter_linux_${FLUTTER_SDK_VERSION}.tar.xz -C /usr/lib/ && \
rm -rf flutter_linux_${FLUTTER_SDK_VERSION}.tar.xz
# Disable Flutter telemetry
RUN flutter --disable-analytics
# Default shell to bash
SHELL ["/bin/bash", "-c"]
Connecting to physical device
I like to debug my apps using a physical device rather than an emulator. When I am connected to home WiFi, I use wireless ADB. I had to enable such option in my phone’s developer options. Then you need to know your phone’s IP-address. I repeat, I would only advise using wireless ADB in a safe private network as you can confuse a request pop up on your phone to allow ADB for someone else’s computer.
If you’re using VSCode, then the extension should prompt you to build the devcontainer and reopen the project directory there. Now you should be able to initialize a flutter project from the devcontainer command line. Then you can give the command
adb connect <phone-ip-address>:5555
to connect to your phone over ADB. Accept the prompt that should have been appeared on the phone. Run flutter devices
to check if the device is connected and go to town! Oh wait, I almost forgot. This probably will not and it is debatable if it even should work on the first try. As we find more and more intricate ways to solve the age-old problem of “But it works on my machine”, the systems themselves fall victim to the same phenomenon. If you run into problems, first use flutter doctor
to see if flutter is working as it should. Read the error logs carefully! The error logs should mostly be readable and point out the package you are missing. Add it to the Dockerfile
and rebuild.
Happy coding!