Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save tridungpham/cf563b62d76d78fcd3f18b9d9ec348e2 to your computer and use it in GitHub Desktop.
Save tridungpham/cf563b62d76d78fcd3f18b9d9ec348e2 to your computer and use it in GitHub Desktop.
Flutter Development on Windows with IntelliJ and WSL

Developing Flutter apps using Windows

I've been developing Flutter apps for two years, and until this month did so exclusively on OS X. I decided to move my development environment entirely to Windows, troubled by Apple's recent behavior with the App Store, put off by being forced to buy an overpriced Mac to develop apps for iOS, and tired of wrestling with OS X's non-standard BSD-like Linux environment and Homebrew package manager. This document describes the various approaches I tried, and how to set up the one I liked the best.

My requirements for a development environment are:

  • IntelliJ must be well supported.
  • I need a Linux-like command line environment with a high quality terminal app. I run Ubuntu on my cloud servers and would prefer to use the same locally for convenience. I would prefer to use apt as my package manager so package names match between my local and development environments.
  • Flutter tools must work smoothly.
  • Builds must be fast.
  • The Android emulator must run natively and perform well.
  • I need a fully fledged Python environment, and the many packages I use must work and install smoothly either through pip or some other package manager. iPython must work.
  • Git must work and perform well.
  • Fastlane must work.

I considered the following options:

  • Use WSL1 and host project files on Windows.
  • Use WSL2 and host project files on Windows, using the /mnt/c drvfs mount point.
  • Use WSL2 and host project files in the VM disk.
  • Use Git Bash, or MSYS2 with pacman as the package manager.
  • Use Windows PowerShell with choco and conda.

After trying each of these out, I am very happy with the approach of using WSL1 with project files hosted in Windows. I'll describe how to set this up, then go over the shortcomings of the other approaches.

WSL1 with Windows-hosted project files

Although I was excited to upgrade from WSL1 to WSL2 when it was released earlier this year, for my purposes, I concluded WSL1 works better (they can in fact coexist without any issues).

There are many advantages to this approach:

  • IntelliJ runs perfectly under Windows.
  • You can use your preferred Linux shell (e.g. zsh with oh-my-zsh and p10k) for command-line tasks, including running Flutter itself.
  • Flutter tools work, with a minor hack described below. Builds are very fast, because you're running Windows Flutter binaries natively against the Windows filesystem.
  • The Android emulator works great, you can debug in IntelliJ.
  • You get the apt package manager, and a complete Python environment. Every package installs and works smoothly.
  • You can run git directly from the WSL1 shell after installing it via apt, it works perfectly (just remember to set core.fileMode to false in your global git config, to avoid issues with Windows/WSL file mode translation) and it runs at native speed.
  • Any tool you like (e.g. fastlane) can be installed and run just as if running Linux.

Basic Setup Instructions

  • Install WSL1. You can follow Microsoft's instructions. I used the excellent LxRunOffline tool to do this, following the instructions here.
    • Install LxRunOffline using Chocolatey with choco install lxrunoffline.
    • Download https://lxrunoffline.apphb.com/download/Ubuntu/focal to get the latest Ubuntu 20 release.
    • Install from the PowerShell with LxRunOffline i -n UF -d c:\WSL\Ubuntu -f <c:\whereabouts_of_the_downloaded_image> -s.
  • Install Windows Terminal Preview and configure it to launch the shell in your WSL installation.
  • Install the Windows version of Flutter from flutter.dev. I put mine in c:\flutter. Be sure to add c:\flutter\bin to your Windows path.
  • Install Android Studio and download the SDK.
  • Install IntelliJ and install the Flutter plugin.
  • Put your project files somewhere under c:\, but not under c:\WSL\Ubuntu to avoid filesystem interaction issues. You can access them from WSL1 in /mnt/c.
  • Install anything you like in your Linux environment, e.g. apt-get install -Y zsh, Oh My ZSH!, powerlevel10k, etc.

Invoking Windows Flutter from the WSL1 shell

The trick to getting this to work with Flutter, is don't run Flutter itself, or any part of the Android SDK, under WSL1. Instead, use WSL1 for the terminal, and defer any Flutter or Android related commands to their Windows equivalents. You can still do this from the WSL1 shell.

Flutter does run under WSL1, but I found build times to be very slow. You can also easily install the Android SDK under WSL1, but can end up in painful situations like having two conflicting adb servers running simultaneously under Windows and WSL1.

If you have installed Flutter at c:\flutter and added c:\flutter\bin to your Windows path, you will find you can already invoke flutter from the WSL1 command line. However, you will get cryptic error messages like this:

/mnt/c/flutter/bin/flutter: line 5: $'\r': command not found

This is because the scripts (including various other scripts it invokes) have been checked out by git in Windows to have Windows-style CRLF line endings. Rather than messing with those, it is easy to arrange for the flutter command to defer to the Windows shell to run the commands; I created a tiny shell script as $HOME/bin/flutter:

#!/bin/bash

cmd.exe /c flutter.bat $@

This assumes flutter.bat is on your Windows path, which WSL1 inherits. Then make sure $HOME/bin is first on your path:

$ export PATH=$HOME/bin:$PATH

Now you can run flutter doctor from the WSL1 command prompt, and it will seamlessly invoke the Windows version. It just works.

WSL2 with project files hosted on the Windows partition

This was the first approach I tried - and seemed the most elegant. IntelliJ can run natively in Windows, while CLI tasks can be done in WSL2. Sadly, it doesn't work well because WSL2's drvfs is too slow. I was seeing 5-10x slower Android builds running flutter from WSL2 compared to native Windows. Even a simple git status command, which is instant running natively in Windows or under WSL1, would take 10 seconds.

WSL2 with project files hosted on the WSL VM disk

While it's great to have a fully fledged Ubuntu kernel running with bare-metal performance within Windows, which means extremely fast build times (faster than Windows because NTFS is comparatively slow relative to ext4), I rejected this approach because:

  • The Android emulator doesn't run under WSL2 because WSL2 doesn't support nested virtualization.
  • If you are using IntelliJ under Windows, you are accessing files via the WSL2 mount point \\wsl$ which is poorly supported by IntelliJ. Issues include slow performance and lack of proper file system monitoring.
  • It's possible to run IntelliJ on the WSL2 side using X410 or some other X server, and the performance is actually not as bad as I expected, with only occasional GUI unresponsiveness. However, the Android emulator needs to run on Windows, and connecting between IntelliJ and the emulator, while possible using adb client/server magic, is awkward and slow.

Note that if you are a VSCode user, your life is made easier by the Remote WSL Extension, which should let you host your project files in WSL2 and enjoy running your IDE on Windows with decent filesystem performance. I haven't tried this personally, and I'm not sure whether things like Android debugging against a Windows-hosted emulator work well, but it seems promising. Unfortunately, there is not yet any equivalent for IntelliJ.

If you want to try this approach with VSCode, there is a nice article describing how to get the emulator working here.

Use Git Bash, or MSYS2 with pacman as the package manager

I liked this approach. Git Bash is built on MSYS2, which is a modern and much improved descendant of Cygwin, so they are essentially the same except MSYS2 includes pacman (the package manager of Arch Linux) so you can install many common packages without having to build them from source. You can compile almost any Linux utility and run it seamlessly under MSYS2, and in that respect it feels very similar to WSL1/2 with less overhead. Flutter builds are extremely fast, Python is fully supported (although I had to go to conda for some packages that were difficult to build under MSYS2), and it essentially feels just like running real Linux.

The real disadvantage, ultimately, is that there aren't many prebuilt packages available in pacman for the MSYS2 platform. Additionally there are actually three MSYS variants, MSYS2, MinGW32, and MINGW64, and not all packages are available for all variants. As a user it's not clear which variant to use, and the differences, although subtle, are important (see Matthieu Vachon's excellent commentary). You can build almost anything yourself, but it can be tedious, and debugging the builds is time consuming. So eventually I rejected this approach since I prefer having the full suite of ready-made Ubuntu packages available via apt.

Use Windows PowerShell with choco and conda as package managers.

This essentially means using Flutter's full Windows toolchain but giving up on a Linux-like CLI. I've never liked PowerShell's syntax (sorry Microsoft), but if you do, this approach does work very well. There are lots of great packages available via choco, and any Python package that doesn't install cleanly via pip probably has a conda package available.

I rejected this approach because I prefer to have just one package manager, rather than juggling three (choco, conda, and pip).

Building for iOS

If you aren't running WSL2, and therefore still have HyperV turned off in Windows, it's relatively easy to run Mac OS as a guest OS under the free VMWare Workstation Player. You can find instructions e.g. here. This is a lot easier than building a Hackintosh, but graphics performance is too slow for a pleasant development experience. It is however acceptable for running iTerm2 for the occasional iOS build or fastlane App Store upload, and it's just barely sufficient for Xcode debugging too. I do not do this personally of course, because it would violate Apple's ToS; I have a late-2013 MBP Retina, one of the last models before they introduced the TouchBar and nerfed the keyboards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment