【Flutter】Docker composeを利用して、WSL環境でも汚さない開発をしてみようと思った

https://docs.flutter.dev/get-started/install

Flutter 環境は Android ビルド環境を整えると、どうしても汚れてしまう。
なので、WSL 上に組もうと思ったが、いっそ Docker を組めばいいと思いついた。

## 方針

Flutter を用いた Android アプリ開発を想定する。

https://docs.flutter.dev/get-started/install/linux/android

必要な言語機能は `asdf` を用いる。
（aptパッケージを紐解くコストが高いので...）

https://asdf-vm.com/ja-jp/


開発環境は `Android` と `Chrome` を想定する。
`Android Studio` は導入しない。

https://developer.android.com/tools/sdkmanager?hl=ja

これらは `WSLg` を用いて、Windows から操作可能なアプリケーションとして利用する。
`WSLg` のおかげで Docker 開発が現実的になったね！

https://learn.microsoft.com/ja-jp/windows/wsl/tutorials/gui-apps

開発は `WSL` にて、実行時は `WSL` 内の Docker を呼び出して実行する。
その時 `WSL` の環境設定を `Docker` コンテナに渡すことで、`WSLg` を利用する。



## Docker コード

まずは枠組みの作成。
`docker` は起動がめんどくさいので `docker compose` を利用するよ。

:::note warn
- docker 内は root で実行しているため、必要に応じて変更が必要
- その他設定項目も外出しはしていないので、各自で調整が必要
    - プロジェクト名は `workspace`
    - 手元の Android が 13 なので `SDK 33` を採用
:::

```bash
$ mkdir flutter-docker
$ cd flutter-docker

$ touch Dockerfile
$ touch docker-compose.yml
```

```yml:/.docker-compose.yml
services:
  flutter:
    build: .
    tty: true
    devices:
      - /dev/kvm:/dev/kvm
    volumes:
      - ./workspace:/workspace
      - /mnt/wslg:/mnt/wslg
      - /tmp/.X11-unix:/tmp/.X11-unix
    environment:
      - DISPLAY=$DISPLAY
      - PULSE_SERVER=$PULSE_SERVER
      - WAYLAND_DISPLAY=$WAYLAND_DISPLAY
      - XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR
```

```dockerfile:./Dockerfile
FROM ubuntu:25.04

WORKDIR /root
ENV HOME /root

# apt install
RUN apt update
RUN apt install -y \
    curl wget vim git xz-utils unzip \
    clang cmake ninja-build pkg-config libgtk-3-dev \
    pulseaudio libnss3 libxkbfile-dev

# asdf
RUN wget https://github.com/asdf-vm/asdf/releases/download/v0.16.7/asdf-v0.16.7-linux-amd64.tar.gz \
    && tar -xzf asdf-v0.16.7-linux-amd64.tar.gz -C /usr/local/bin \
    && rm asdf-v0.16.7-linux-amd64.tar.gz
ENV PATH $PATH:$HOME/.asdf/shims

# asdf flutter, java
RUN asdf plugin add flutter \
    && asdf install flutter 3.29.3-stable \
    && asdf set --home flutter 3.29.3-stable \
    && git config --global --add safe.directory /root/.asdf/installs/flutter/3.29.3-stable \
    && flutter config --no-analytics \
    && asdf plugin add java \
    && asdf install java openjdk-21.0.2 \
    && asdf set --home java openjdk-21.0.2 \
    && bash ~/.asdf/plugins/java/set-java-home.bash

# android sdk
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip \
    && unzip ./commandlinetools-linux-13114758_latest.zip \
    && mkdir -p ~/android-sdk/cmdline-tools/latest \
    && mv ./cmdline-tools/* ~/android-sdk/cmdline-tools/latest \
    && rm ./commandlinetools-linux-13114758_latest.zip
ENV PATH $PATH:$HOME/android-sdk/emulator:$HOME/android-sdk/cmdline-tools/latest/bin

RUN yes | sdkmanager --licenses >/dev/null \
    && sdkmanager --install platform-tools \
    && sdkmanager --install emulator \
    && sdkmanager --install "build-tools;33.0.0" "platforms;android-33" "system-images;android-33;default;x86_64" \
    && echo no | avdmanager create avd -n android33 -k "system-images;android-33;default;x86_64" \
    && flutter config --android-sdk ~/android-sdk
# RUN emulator @android33 -no-metrics

# chrome
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
    && apt install -y --fix-missing ./google-chrome-stable_current_amd64.deb \
    && rm ./google-chrome-stable_current_amd64.deb
# RUN google-chrome --no-sandbox

# api clear
RUN apt clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

CMD ["bash"]
```

Dockerfile の作成は、素の Ubuntu に手で入れたものを書き連ねただけ...。
（最適化を目指さなければ）実はそんなに難しくない。
あんまり固めすぎると、途中でイレギュラーが発生したときに辛いので、適度に最適化しています。

`android33` の部分は、自身の環境に合わせて調整してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833190/6d15ca56-3439-4e5b-9126-924e52bdd30e.png)

https://developer.android.com/guide/topics/manifest/uses-sdk-element


## 実行方法

初回 build には私環境で10分くらいかかります...。

```bash
# <初回> ビルド
$ docker compose build --no-cache

# Docker の起動
$ docker compose up -d

# <初回> flutter 環境構築
$ docker compose exec flutter flutter doctor
  Doctor summary (to see all details, run flutter doctor -v):
  [✓] Flutter (Channel stable, 3.29.3, on Ubuntu 25.04 5.15.167.4-microsoft-standard-WSL2, locale en_US)
  [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
  [✓] Chrome - develop for the web
  [✓] Linux toolchain - develop for Linux desktop
  [!] Android Studio (not installed)
  [✓] Connected device (3 available)
  [✓] Network resources
  
  ! Doctor found issues in 1 category.
$ docker compose exec flutter bash
$ cd ../
$ flutter create workspace
$ flutter pub get
$ chmod -R 777 ./workspace
$ exit

# エミュレーターの起動 (Android か Chrome どちらかで良い)
$ docker compose exec flutter emulator @android33 -no-metrics
$ docker compose exec flutter google-chrome --no-sandbox

# flutter の起動
$ docker compose exec flutter flutter run
```

開発体験の向上に感動する。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833190/438fa23a-1b53-4d27-9007-5e654b388bba.png)


最終系はこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833190/5c9f012d-a34b-459a-8f59-380f27c7eaa9.png)



## おわりに

作ってて思ったが Flutter は wsl 内に閉じ込めても良かったかもしれん...。
VSCode の拡張機能が Flutter の SDK を要求してくるのよね。

あと asdf 仕様変わりすぎ！！！
今度記事を書こう。




## 参考記事

### 全体的な参考

- Android Studioなしでflutter環境構築 - 徒然
    - https://tozen.me/stacks/flutter/no-android-studio-flutter-environment-setup
    - 神の記事
- android - error: package path is not valid. valid system image paths are:ository... null - Stack Overflow
    - https://stackoverflow.com/questions/66597053/error-package-path-is-not-valid-valid-system-image-paths-areository-null
    - `system-images` がうまくダウンロードできなかった対策
- android - How to pass KVM from local machine to docker container? - Stack Overflow
    - https://stackoverflow.com/questions/65435561/how-to-pass-kvm-from-local-machine-to-docker-container
    - `KVM` がうまく動作しなかった対策
- 【コマンド5発】WSL2にGoogle Chromeをインストール
    - https://zenn.dev/hrt999/articles/32d51711cb2926
    - `Chrome` の導入方法

### Linux 環境

- /bin, /usr/bin, /usr/sbin, /usr/local/bin の違いとは？ #Linux - Qiita
    - https://qiita.com/valzer0/items/67a4c8bf2b1be0fc825a
- DockerfileでPATHを通す時はRUNではなくENVを使おう #Scala - Qiita
    - https://qiita.com/TakashiOshikawa/items/2b8974c242a7054e0be5
- wslのDockerでGUI操作 #WSL - Qiita
    - https://qiita.com/rayfiyo/items/17842dcf258b4d585531