My Linux Development Environment for Flutter Android Apps
Why I use Flutter to create Android apps
The official tool to create Android applications is Android Studio, which is available on Linux. This tool is an IDE based on IntelliJ Idea. But what if I want to use another IDE and not rely on one that needs a lot of resources to work (according to the documentation at least 8GB RAM required and 32GB recommended!)?
I am a Neovim user and I find it great software. It’s by default lightweight, highly customizable, and can be turned as a powerful IDE with all the features you could expect (LSP, formatters, debuggers…) thanks to plugins. Yet don’t try to directly replace Android Studio with Neovim. The former, while requiring a lot of resource to work, is still the best tool to manage and integrate all the components needed to run Android applications (that is, XML files for UI resources, the Java/Kotlin code, device emulation, Gradle…).
Flutter is a framework that allows you to build
cross-platform applications (for Android, iOS, Windows, Linux…) thanks to an
single programming language called Dart. What I love with
Flutter is that you can tell how the UI of your app works by just looking at
the Dart code. For Android projects written in Java/Kotlin, I find it more
difficult to figure out the UI layout just by looking at the code, especially
because it relies on both XML files and Java/Kotlin code (actually a new
toolkit called Jetpack Compose allows
in the same way as Dart to have a declarative UI that you can guess just by
reading Kotlin code. But the ecosystem of that toolkit is still very tied to
Android Studio). The main reason I’m using Flutter is because it’s very easy to
code Flutter apps in Neovim thanks to the
flutter-tools.nvim
plugin.
Why I don’t want to install Flutter on my Linux environment
If you install and use Flutter on your machine, multiple new files and directories will be created on your home folder:
$HOME
├── .cache
├── .config
├── .dart-tool
├── .dartServer
├── .flutter-devtool
├── .local
├── .pki
├── .pub-cache
├── .ssh
├── Desktop
├── Documents
├── Downloads
├── Music
├── Pictures
├── Public
├── Templates
├── Videos
├── .bash_profile
├── .bashrc
└── .flutter
As above you can see 5 Flutter and Dart files and directories in your
$HOME: .dart-tool, .dartServer, .flutter-devtool, .pub-cache and
.flutter. This leads to a disorganized $HOME folder, especially when the
majority of programs follows the “XDG Base Directory”
specification which
makes storing all of this kind of files and folders more organized. If you
don’t know about that specification, the goal is to keep configuration and
cache files of programs inside either ~/.config, ~/.cache, ~/.local/share
or ~/.local/state, so that the home directory stays clean:
$HOME
├── .cache
├── .config
├── .local
├── .pki
├── .ssh
├── Desktop
├── Documents
├── Downloads
├── Music
├── Pictures
├── Public
├── Templates
├── Videos
├── .bash_profile
└── .bashrc
Flutter developers know they are not following the XDG specification:
You can find GitHub users complaining about this since 2020, and as of now
(beginning of 2026 when I’m writing this), the issue is still there. The devs
will in a not too distant future give users the ability to specify through an
environment variable a common directory for all the above Flutter and Dart
files and folders (that is, for instance keep all of them inside
~/.local/share/dart. This is great, but it does not follow to the letter the
XDG specification).
So instead of waiting 6 years for an issue to be fixed, I decided to deploy Flutter in a VM so that my home directory stays clean. And to be honest, I don’t really want to run in my Linux distro a program that enables telemetry by default (“opt-out”). Yes Flutter is free software, but this does not inspire confidence. So let’s isolate Flutter!
Installing a VM with Flutter in it
Why a VM
So the main goal is to isolate Flutter so that it never interacts with my home directory. There are multiple ways to isolate processes in Linux, and here I will use virtual machines. You might ask why not OCI containers like Docker. The reason is that we need a persistent environment because we’ll install many tools (see below) used to build Android apps. I’m aware you can use Docker volumes to persist data, but here using a VM seems to me the cleanest way to create our Flutter dev environment.
You might also know how to use a LXC container, which is more lightweight than virtual machines and which can be great for what we want to do. If you know how to use this and want a lighter solution, go for it. Here I will focus on what’s the most popular, that is, virtual machines.
Creating the VM with virt-install
To run virtual machines in Linux, the best tool according to me is
libvirt. I will install the VM with
virt-install and manage it with virsh, but you might find it better to use
a graphical UI. In that case I highly recommend virt-manager. I will not
describe how to install all these tools, please refer to the official
documentation as the installation might depend on your own Linux distro. You
can still look at the great Arch Linux
wiki.
Please choose the Linux distro you like for the VM. Neither Flutter nor Android
official doc recommend using a specific distro. They do reference apt-get
(and also yum for Android doc) though 1 2. Also the official
documentation to build AOSP (which is not the goal here) uses Ubuntu 18.04 or
later 3. So the safe choice here would be to use Ubuntu LTS. As for me I
will use Arch Linux because I’m used to it and because I want to install the
latest version of Neovim.
Download a cloud image of the distro you chose for the VM, with cloud-init
installed (not mandatory but highly recommended to simplify the process of
setting up the VM).
mkdir -p ~/Documents/Flutter/dev-vm
cd ~/Documents/Flutter/dev-vm
# For Arch, go check
# https://gitlab.archlinux.org/archlinux/arch-boxes/-/packages to get the
# up-to-date URL below
wget "https://gitlab.archlinux.org/archlinux/arch-boxes/-/package_files/11243/download"
We check the integrity of the image we’ve just downloaded. For Arch, check again the above GitLab link so to retrieve:
- the image signature;
- the file containing the SHA256 checksum of the image;
- the signature of the SHA256 file.
To verify the checksum:
sha256sum -c Arch-Linux-x86_64-cloudimg-20260301.495047.qcow2.SHA256
To verify the signature, the public key used to sign the files is here. Run:
gpg --import arch-key.asc
gpg --verify Arch-Linux-x86_64-cloudimg-20260301.495047.qcow2.sig
gpg --verify Arch-Linux-x86_64-cloudimg-20260301.495047.qcow2.SHA256.sig
Create a SSH key that will be used to connect to the VM:
ssh-keygen -f ~/.ssh/id_ed25519_flutter -C host
# Show the public key. We'll use it later
cat ~/.ssh/id_ed25519_flutter.pub
Create a user-data.yaml file that contains the following. The idea here is to
create two users, one name arch as a system administrator of the VM, and one
named flutter used to run Flutter commands. We also install dependencies for
Flutter 1 and for the Android tools used to build apps. Finally we append a
line to /etc/fstab to mount in the VM a shared storage so that we can
retrieve our Flutter projects inside the VM.
#cloud-config
users:
- name: flutter
ssh_authorized_keys:
# Put the above SSH public key
- ssh-ed25519 ... host
- name: arch
sudo: 'ALL=(ALL:ALL) NOPASSWD: ALL'
ssh_authorized_keys:
# Put the above SSH public key
- ssh-ed25519 ... host
packages:
# In Arch, curl and xz are already installed
#- curl
- git
- unzip
#- xz
- zip
- glu
- which
- wget
- jdk17-openjdk
# Neovim and some useful tools
- neovim
- less
- gcc
- fd
write_files:
- path: /etc/fstab
append: true
defer: true
content: |-
shared_mount /home/flutter/my-project virtiofs defaults 0 0
We install the VM with the following command. Replace
$HOME/Documents/Flutter/my-projects with the path to your Flutter projects.
virt-install --connect qemu:///system \
--name flutter-dev \
--network network=default \
--memory 8192 \
--vcpus 4 \
--osinfo archlinux \
--import \
--noreboot \
--cloud-init user-data="$HOME/Documents/Flutter/dev-vm/user-data.yaml" \
--disk pool=default,size=20,backing_store="$HOME/Documents/Flutter/dev-vm/Arch-Linux-x86_64-cloudimg-20260301.495047.qcow2" \
--memorybacking source.type=memfd,access.mode=shared \
--filesystem source="$HOME/Documents/Flutter/my-projects",target=shared_mount,driver.type=virtiofs
The installation takes some time. You can tell it’s done once the VM shows [ OK ] Reached target Cloud-init target.. Check cloud-init has installed
everything. Open another terminal, log in with arch, update the VM and shut
it down:
# We retrieve the local IP address of the VM with:
virsh --connect qemu:///system domifaddr flutter-dev
ssh -i ~/.ssh/id_ed25519_flutter arch@192.168.122.123
# Inside the VM
sudo pacman -Syu
sudo poweroff
Flutter and Android command line tools installation
Start the VM and log in as flutter:
virsh --connect qemu:///system start flutter-dev
ssh -i ~/.ssh/id_ed25519_flutter flutter@192.168.122.123
We install Flutter 1:
mkdir ~/develop
cd ~/develop
# Check Flutter's website to get the up-to-date URL
wget "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.41.4-stable.tar.xz"
tar -xf flutter_linux_3.41.4-stable.tar.xz -C .
echo 'export PATH="$HOME/develop/flutter/bin:$PATH"' >> ~/.bashrc
# Log out then log in, then check the installation worked
flutter --version
flutter --disable-analytics
dart --version
Then install Android command line tools, used to build the apps 4.
Note that the tools are installed thanks to an archive that contains these
tools inside the cmdline-tools/bin folder. This causes some tools not working
properly because they expect the path cmdline-tools/latest/bin instead. To
fix this, we first install the command line tools with the archive, then
reinstall these with sdkmanager that will fix the path, and finally remove
the tools installed with the archive.
cd ~/develop
# Below link comes from "https://developer.android.com/studio", secton "Command
# line tools only". Check the URL is up-to-date.
wget "https://dl.google.com/android/repository/commandlinetools-linux-14742923_latest.zip"
unzip commandlinetools-linux-14742923_latest.zip -d ~/develop/temp-android-sdk
mkdir -p ~/Android/Sdk
~/develop/temp-android-sdk/cmdline-tools/bin/sdkmanager --sdk_root=$HOME/Android/Sdk/ "cmdline-tools;latest"
rm -rf ~/develop/temp-android-sdk
echo 'export PATH="$HOME/Android/Sdk/cmdline-tools/latest/bin:$PATH"' >> ~/.bashrc
# Log out then log in
sdkmanager "platforms;android-36.1" "build-tools;36.1.0" "platform-tools"
# Note that Flutter expects other tools such as cmake and NDK. It's fine
# because the former will automatically install the missing components when
# running `flutter build apk`
# Check Flutter can find the Android SDK tools
flutter doctor
# Accept Android licenses
flutter doctor --android-licenses
Neovim
Install your own Neovim configuration.
If you have none, you can for instance install Lazyvim (Neovim distribution) and add the Flutter plugin:
git clone https://github.com/LazyVim/starter ~/.config/nvim
rm -rf ~/.config/nvim/.git
cat <<EOF > ~/.config/nvim/lua/plugins/flutter.lua
return {
{
"nvim-flutter/flutter-tools.nvim",
lazy = false,
dependencies = { "nvim-lua/plenary.nvim" },
config = true,
},
}
EOF
Useful commands to manage the VM
# To start the VM
virsh --connect qemu:///system start flutter-dev
# To stop the VM (yes you need to use `destroy`)
virsh --connect qemu:///system destroy flutter-dev
# To log in
virsh --connect qemu:///system domifaddr flutter-dev
ssh -i ~/.ssh/id_ed25519_flutter flutter@192.168.122.123
In order to avoid writing --connect qemu:///system every time, you can use
the environment variable LIBVIRT_DEFAULT_URI 5.
Or use virt-manager if you want a GUI to manage the VM.
How to test your Flutter apps on web platforms
You need SSH port forwarding:
ssh -i ~/.ssh/id_ed25519_flutter -L 8080:localhost:8080 flutter@192.168.122.123
# Inside the VM
cd /path/to/flutter/project
flutter run -d web-server --web-port 8080
Drawbacks of my development environment
- You need to manage and maintain a VM to use the IDE, and you need to use every time SSH to connect to the VM (you can simplify this process by using Neovim remote capabilities if you want);
- You can’t sign your commits or use
git pushin the VM if you want to keep your GPG or SSH keys outside the VM; - You need to configure Neovim inside the VM, which gives two Neovim configurations to manage along the one on your host machine;
- Notice how I did not mention how to run emulated Android devices inside the VM. The reason is that nested virtualisation is not a good idea. You will have performance issues if you try so. I advice using another method to test Android apps, like using a physical device in developer mode, and connect it to the VM thanks to USB redirection.
Despite all of these drawbacks, my home directory stays clean!
* Next >