WSL2でFlutter環境をできるだけクリーンに構築する(えみ)

# たどり着いたGoal

- [公式のインストール手順](https://docs.flutter.dev/get-started/install/windows)がwindows環境にそのままインストールしていたのでWindows環境(WSL2)でAndroid実機でデバッグできるFlutter開発環境を立ち上げた
- 既存のWSL2環境を壊さずに＆Windows側の環境を極力壊さずにクリーンに構築できた
- IDEにはVSCを利用する（Android Studioはインストールせずに構築できた）
- Android実機は `AQUOS sense3 basic SHV48` を利用（Android 11にアップグレード済）[^au]

[^au]: AU端末は[各ADBドライバ一覧がまとまっている＆リンク先がきちんとしている](https://www.au.com/developer/android/kaihatsu/adb/)のが開発者に優しくていいですね！（他社だと大体メーカーサイトトップへのリンクしかないことが多い）

# やっていないこと

- 以下構築手順ではWindows側はコマンドプロンプトしか使っていません（私がそれしか使えないので）[^terminal]
- iOS版のビルド（基本的にはAndroid側で開発する想定）
- 【重要】Androidエミュレータとの連携
  - 実機でやればいいよねの精神
  - 一応windows側に開発環境を別途構築して各々のADBをTCP接続すればできないこともないはず（試していない）

# 構築手順

## 1. Flutter用の開発環境をWSL2上に新規構築する（仮想マシンのインポート）

まっさらな開発環境を構築するため、WSLコマンドを使って仮想マシンをインポートします。
まずは現時点(2021/12/18)での最新版であるUbuntu 21.04のクラウドイメージを[ここ](https://cloud-images.ubuntu.com/releases/hirsute/release/)からダウンロードしましょう。

ファイル名が `～wsl.rootfs.tar.gz` となっているものの中から自分のCPUに合わせて選びましょう。

ダウンロードできたら下記のようにWSLコマンドでインポートします。なお、インポート先のディレクトリ（以下の例では `%HOMEPATH%\wsl\env\flutter-env`） は先に作成しておきましょう。

ここでは、仮想環境名`flutter-env`で、WSH2(`--version 2`)でインストールします。 

```:コマンドプロンプト
> wsl --import flutter-env %HOMEPATH%\wsl\env\flutter-env %HOMEPATH%\wsl\rootfs\ubuntu-21.04-server-cloudimg-amd64-wsl.rootfs.tar.gz --version 2
```

インポート結果を確認します。下記コマンドで `flutter-env` が確認できると思います。

```:コマンドプロンプト
> wsl -l -v
```

確認出来たら下記コマンドで起動できます。

```:コマンドプロンプト
> wsl -d flutter-env
```

※[よく使うWSLコマンド](### 2-3. 仮想環境の操作でよく使った＆すぐに忘れるコマンドメモ)

## 2. WSL環境の事前準備

### 2.1 一般ユーザの作成

上記手順でWSL環境にログインするとrootでログインしますが、あとあと面倒になるので先に一般ユーザを作成しておきます。以下の例では `wsl-user` を追加しています。

```:WSL
# useradd -m -s $(which bash) wsl-user
# passwd wsl-user
# usermod -G sudo wsl-user
# exit
```

再度ログインしましょう

```:コマンドプロンプト
> wsl -d flutter-env -u wsl-user
```

これで `wsl-user` でログインすることができますが毎回のユーザ指定が面倒なら仮想マシン上で `/etc/wsl.conf` を以下のように設定すればＯＫです。なお修正するにはroot権限が必要なので `sudo vi /etc/wsl.conf` な感じで修正してください（初期設定時にはファイルがないので新規作成でOKとなります）

```:WSL
[user]
default=wsl-user
```

またついでにWindowsのPATHを引き継がないようにしたほうが後で楽です。結果 `/etc/wsl.conf` は以下としましょう。

```:WSL
[user]
default=wsl-user
[interop]
appendWindowsPath = false
```

設定後ログアウトして仮想マシンを再起動させれば適用されます。

```:WSL
$ exit               <-- Ubuntu環境から抜ける
> wsl -t flutter-env <-- 仮想マシンを停止
> wsl -d flutter-env <-- 仮想マシンを起動＆ログイン
```

またこれはオプションですが、仮想マシンにログインすると初期ディレクトリがWindows実行ユーザのWindowsホームディレクトリとなっていると思います。これはこれで気持ち悪いので下記のように `.bashrc` に書いておくと少しだけ幸せになります。

```:WSL
$ echo 'cd ~' >> ~/.bashrc
```

またWindowsのPATHを引き継がなくしましたが、 `explorer.exe` コマンドは `explorer.exe .` でUbuntu内のディレクトリをexplorerで開けるので地味に便利です。そこで一般的なWindowsのコマンドが使えるように `/mnt/c/WINDOWS/` を追加しましょう。
また後でVisual Studio Codeも利用したいのでパスを通しておきます。

:loudspeaker: 重要: ドライブ指定や [user_name] はご自身の環境にあわせて書き換えてください。

```:WSL
$ echo export PATH=\$PATH:/mnt/c/WINDOWS/ >> ~/.bashrc
$ echo export PATH=\$PATH:\"/mnt/c/Users/[user_name]/AppData/Local/Programs/Microsoft VS Code/bin\" >> ~/.bashrc
```

ここまで済んだらいったんシェルを再起動しましょう。ログイン時の初期ディレクトリが `/home/wsl-user` になっていることが確認できると思います。

```:WSL
> exec $SHELL -l
> $ pwd
/home/wsl-user
```

これでOKです。

### 2.2 WSL環境の事前準備(Ubuntuの最新化)

普通にaptを使って最新化しましょう。

```:WSL
$ sudo apt update -q; sudo apt upgrade -yq
```

また、この後の処理に備えて下記コマンドをインストールしておきましょう。

```:WSL
$ sudo apt install unzip
```

これで基本的な環境は整いました。Flutter環境構築は結構大掛かりな作業となりそうなので、この時点でいったん仮想マシンをエクスポートしておきました（スナップショットを取っておく感じです）

エクスポート先およびファイル名は下記の例では `%HOMEPATH%\wsl\rootfs\flutter-env.tar` です。

```:WSL
$ exit
> wsl -t flutter-env <-- 仮想マシンを停止
> wsl -l -v
> wsl --export flutter-env %HOMEPATH%\wsl\rootfs\flutter-env.tar
```

エクスポート先に対象ファイルが作成されていればスナップショットの作成も完了です。ちなみに私の環境では1.2Gくらいのファイルが作成されました。

### 2-3. 仮想環境の操作でよく使った＆すぐに忘れるコマンドメモ

- 仮想環境の一覧
  - `wsl -l -v`
- 仮想環境を起動する
  - `wsl -d flutter-env`
- 仮想環境を停止する
  - `wsl -t flutter-env`

## 3. Flutter環境の構築

エクスポート(というかスナップショット取得)も完了したのでWSL環境に再度ログインします。

```:コマンドプロンプト
> wsl -d flutter-env
```

### 3-1. JavaSDK

JavaSDKのインストールと環境変数をセットします。

※JAVA_HOMEのパスはきちんと存在するか確認してください。

```:WSL
$ sudo apt update && sudo apt install default-jdk -y
$ export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
$ export PATH=$PATH:$JAVA_HOME/bin
$ printf "\n\nexport JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\nexport PATH=\$PATH:\$JAVA_HOME/bin\n" >> ~/.bashrc
```

設定後、下記で確認してみてください！

```:WSL
> exec $SHELL -l
> $ echo $JAVA_HOME
/usr/lib/jvm/java-11-openjdk-amd64
> $ echo $PATH
...省略
```

環境変数が表示されたらOKです！

### 3-2. Android開発環境

AndroidSDKをインストールします。今回はAndroid Studioはインストールしないので、Command line toolsをダウンロードします。なお、下記サイトから利用規約に同意したうえでダウンロードリンクを取得してください。
https://developer.android.com/studio/index.html#downloads

また、 commandlinetools-`linux`-xxxxxxx_latest.zipと、WSL環境ではlinuxを選択してください。

```:WSL
$ mkdir -p ~/Android/SDK/cmdline-tools
$ wget https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip -O latest.zip
$ unzip latest.zip
$ mv cmdline-tools ~/Android/SDK/cmdline-tools/latest
$ rm -rf latest.zip
$ export ANDROID_HOME=$HOME/Android/SDK
$ export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
$ printf "\nexport ANDROID_HOME=\$HOME/Android/SDK\nexport PATH=\$PATH:\$ANDROID_HOME/cmdline-tools/latest/bin\n" >> ~/.bashrc
$ sdkmanager
[=======================================] 100% Computing updates...
```

`sdkmanager` を実行し上記のような表示でエラーがでなければ次のコマンドでadb(Android Debug Bridge)が使えるようにしましょう。

```:WSL
$ sdkmanager --install "platform-tools"
$ export PATH=$PATH:$ANDROID_HOME/platform-tools
$ printf "export PATH=\$PATH:\$ANDROID_HOME/platform-tools\n" >> ~/.bashrc
$ adb
Android Debug Bridge version 1.0.41
Version 31.0.3-7562133
Installed as /home/wsl-user/Android/SDK/platform-tools/adb

global options:
 -a         listen on all network interfaces, not just localhost
…
```

`adb` を実行し上記のようにhelpがでればOKです。

次は `sdkmanager --list` で選択できるイメージを選択します。

```:WSL
$ sdkmanager --list
Installed packages:=====================] 100% Computing updates...
  Path           | Version | Description                | Location
  -------        | ------- | -------                    | -------
  platform-tools | 31.0.3  | Android SDK Platform-Tools | platform-tools

Available Packages:
  Path                                                                                     | Version      | Description 
  -------                                                                                  | -------      | -------     
  add-ons;addon-google_apis-google-15                                                      | 3            | Google APIs 
  add-ons;addon-google_apis-google-16
…
```

本記事では `AQUOS sense3 basic SHV48` の最新版である、Android 11(APIレベル30)をインストールしました。

```:WSL
$ sdkmanager --install "system-images;android-30;google_apis;x86" "platforms;android-30" "build-tools;30.0.3"
$ sdkmanager --licenses
5 of 6 SDK package licenses not accepted. 100% Computing updates...
Review licenses that have not been accepted (y/N)? y
…
```

同意を求められるので内容をきちんと読んでから:wink: 合意してください。
これでAndroidの開発環境は整いました。

### 3-3. Flutter環境

やっと本命のFlutter環境の構築です。基本は[公式サイト](https://flutter.dev/docs/get-started/install/linux#install-flutter-manually)の手動インストールを参考にします。インストールするときは、最新のバージョンを指定してください。今回は `2.8.1` でインストールします。

```:WSL
$ wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_2.8.1-stable.tar.xz
$ tar xf flutter_linux_2.8.1-stable.tar.xz
$ mkdir ~/Flutter
$ mv flutter ~/Flutter/SDK
$ export FLUTTER_ROOT=$HOME/Flutter/SDK
$ export PATH=$PATH:$FLUTTER_ROOT/bin
$ printf "\nexport FLUTTER_ROOT=\$HOME/Flutter/SDK\nexport PATH=\$PATH:\$FLUTTER_ROOT/bin\n" >> ~/.bashrc
$ flutter --version
Flutter 2.8.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 77d935af4d (33 hours ago) • 2021-12-16 08:37:33 -0800
…
```

上記でエラーがでなければインストール成功です。 `flutter-dockor` の結果は以下のような感じです。

```console
$ flutter doctor
Running "flutter pub get" in flutter_tools...                       3.3s
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.8.1, on Ubuntu 21.04 5.10.60.1-microsoft-standard-WSL2, locale C.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[!] Android Studio (not installed)
[!] Connected device
    ! No devices available

! Doctor found issues in 3 categories.
```

上記のようになれば問題ないです！

### 3-4. Visual Studio Code

WSL2の環境でVSCodeを起動します。なお、もし `Command 'code' not found` といわれる場合は環境変数 `$PATH` を確認してください。おそらく[user name]を置換し忘れているかと。

```:WSL
$ code .
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/f8135ab2-eeb9-8450-43d5-c2a632a18546.png)

うまく接続されています。機能拡張でFlutterをインストールしてください。私の場合はFlutterをインストールするとDartもインストールされました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/75ef8865-7743-d358-d00b-9c76889a801c.png)

それでは新しいプロジェクトを作ってみます。 `Ctrl + Shift + P` から「Flutter: New Application Project」を選択してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/f2586083-2055-1ccf-e1b3-0b59bc4d6cfa.png)

まずはWebServerで動かしてみましょう。VSCodeの画面から `Run -> Start Debugging` をクリックしてください。しばらく待たされた後Chromeが起動して真っ白の画面のままで、VSCodeのDEBUG CONSOLEには

> Please click the Dart Debug extension button in the spawned browser window

と表示されると思います。いわれる通りChromeに機能拡張 `Dart Debug Extension` を入れてから機能拡張のボタンをクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/d1e454d7-2943-b595-6417-03a922f80179.png)


下記の画面が表示されると成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/ebe0eadd-979e-5fa7-2163-0d997af8aff3.png)

:loudspeaker: 重要: chromeの機能拡張 `Dart Debug Extension` を入れないと画面は白いままでエラーも出ません。

## 4. Androidの実機を接続する

### 4.1 Android 11以降の場合(リモートデバッグ)

Android11以降であれば直接USBで接続するより、リモートデバッグのほうが簡単です。
なお直接USB接続する場合は追加で以下の手順が必要です。詳細は[こちらに](### 4. 番外編: Androidの実機を接続する(Android 10以前方法))

- windows側にもadbコマンド(command line tools)をインストールする必要がある
- adbコマンドを動かすにはjavaのインストールも必要…
- windows側にADBインタフェースのドライバが必要
- windows側のadbとWSL2側のadbをTCPで接続する必要ががある

今回の端末はAndroid 11の端末なので、下記の記事に従って接続します。
https://developer.android.com/studio/command-line/adb#connect-to-a-device-over-wi-fi-android-11+

Android側のリモートデバッグをONにします。手順は上記記事を参照してください。ワイヤレスデバッグ画面にて「ペア設定コードによるデバイスのペア設定」を表示しメモしてください。例えば以下のような表示だったとします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/c2fab4b8-4f16-a48a-a19c-1b0dddfbc8e5.png" width=50%>

この場合は以下のコマンドでペアリングします。

```:WSL2
$ adb pair 192.xxx.xxx.xxx:34326

Enter pairing code: 871748
Successfully paired to 192.xxx.xxx.xxx:34326 [guid=adb-xxxxxxxx-xxxxxx]
```

次は接続です！接続する場合のポート番号はワイヤレスデバッグで表示される下記ポート番号を利用します。

:loudspeaker: 重要: 通常ペアリングのポート番号と異なります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/b9599c97-a345-f8e9-091d-0ffd18d5c789.png" width=50%>

```:WSL2
$ adb connect 192.xxx.xxx.xxx:43739
connected to 192.xxx.xxx.xxx:43739
```

あとは接続を確認してみましょう。

```:WSL2
$ flutter devices
1 connected device:

SHV48 (mobile) • 192.xxx.xxx.xxx:43739 • android-arm64 • Android 11 (API 30)
```

Flutterから認識されいますね！

### 4-2. デモアプリを実機で動かす。

それではデモプロジェクトをVSCodeで開きましょう。

```:WSL2
$ cd PATH_TO_PROJECT
$ code .
```

あとは、WebServerで動作確認したときと同じく、VSCodeの画面から `Run -> Start Debugging` をクリックしてください。下記のようになると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/00555f32-883b-3d9c-7c29-c517d13d5df1.png)

しばらく待つと(結構時間がかかります)…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/7d36e7ad-ea5d-6e53-650e-607bcc1cef46.png)

実機でアプリが立ち上がりました！！！
※上のスクリーンショットだと Android 30.0.3が入っていなかったみたいですね。試行錯誤したので本記事と差異がでていますがお気になさらず:relaxed:

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/5225f013-d3db-4d2d-7fcd-86bb7f8993e0.png" width=50%>

最後に切断するには下記でOKです。

```:WSL2
$ adb disconnect
disconnected everything
```

### 4.3. 番外編: Android 10以前方法の接続方法(USB接続)

今回はこれで開発環境が整いましたが、一応Android 10以前の方法も試してみます。基本的には下記の記事をなぞればOKです。

https://developer.android.com/studio/command-line/adb#wireless

この方法を試す場合は、Windows側でもADBコマンドの実行ができなければなりませんので `できるだけクリーンに` には反しますが、最低限のインストールで試すことにします。

まずはJavaをインストールします。下記から `Ready for use...` で指定された最新版をダウンロードしてください。

https://jdk.java.net/

ダウンロード後適切な場所に配置しWindows側の環境変数 `JAVA_HOME` を設定してください。
私の環境では以下のようにしました。

```:コマンドプロンプト
> echo %JAVA_HOME%
D:\jdk-16.0.1
```

次に、下記からAndroid開発環境のコマンドラインツールをダウンロードします。
https://developer.android.com/studio#downloads

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/fb3dfb7f-c7c8-2a50-35ec-45b31d8aa70e.png)

ダウンロードしたファイルを展開後、適切な場所に配置してください。どうせそれほど使わないのでパスは通さなくてよいですが、環境変数 `ANDROID_HOME` は必須です。

私はWSL2環境と同じように下記としました。展開したコマンドラインツールは `cmdline-tools\bin` となっていたので `latest` を間に挟んでいます。

これで実行できると思います。

```:コマンドプロンプト
> cd D:\Android\SDK\cmdline-tools\latest\bin
> sdkmanager.bat --list
```

リストが表示されれば成功です！あとは `adb.exe` が使えるように最低限[^atleast]のインストールをします。バージョンはWSL環境とあわせました。

```:コマンドプロンプト
> sdkmanager.bat --install "platforms;android-30" "platform-tools"
```

インストールに成功すると `ANDROID_HOME` 配下に `platforms` フォルダが追加されるはずです。

```:コマンドプロンプト
> dir D:\Android\SDK
...省略
2021/07/03  15:42    <DIR>          .
2021/07/03  15:42    <DIR>          ..
2021/07/03  15:42    <DIR>          .temp
2021/07/03  15:21    <DIR>          cmdline-tools
2021/07/03  15:42    <DIR>          licenses
2021/07/03  15:43    <DIR>          platforms
```

下記フォルダにコマンドがあるはずなのでコマンド実行してみてください。

```:コマンドプロンプト
> cd D:\Android\SDK\platform-tools\
> dir
...省略
2021/07/03  15:47    <DIR>          .
2021/07/03  15:47    <DIR>          ..
2021/07/03  15:47         5,937,152 adb.exe
2021/07/03  15:47            97,792 AdbWinApi.dll
2021/07/03  15:47            62,976 AdbWinUsbApi.dll
2021/07/03  15:47    <DIR>          api
2021/07/03  15:47           243,200 dmtracedump.exe
2021/07/03  15:47           439,296 etc1tool.exe
2021/07/03  15:47         1,630,208 fastboot.exe
2021/07/03  15:47            43,520 hprof-conv.exe
2021/07/03  15:47           231,594 libwinpthread-1.dll
2021/07/03  15:47           495,616 make_f2fs.exe
2021/07/03  15:47           495,104 make_f2fs_casefold.exe
2021/07/03  15:47             1,157 mke2fs.conf
2021/07/03  15:47           755,712 mke2fs.exe
2021/07/03  15:47           701,027 NOTICE.txt
2021/07/03  15:47            17,783 package.xml
2021/07/03  15:47                38 source.properties
2021/07/03  15:47         1,217,536 sqlite3.exe
2021/07/03  15:47    <DIR>          systrace
> adb.exe
```

`adb.exe` を実行しhelpがでればOKです。

次はAndroid端末をWindowsに `物理的に` 接続します。今回は `AQUOS sense3 basic SHV48` を接続しますので下記サイトを参照にドライバをインストールしました。

http://k-tai.sharp.co.jp/support/developers/driver/

デバイスマネージャーで下記のように表示されればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/78947883-8455-55c5-609e-516833271c94.png)

あとはAndroid端末のデバッグモードは有効にしておいてください[^debugmode]

この状態（ドライバを入れた＆USBで有線接続されている）であればADBで一覧に表示されるはずです。

```:コマンドプロンプト
> D:\Android\SDK\platform-tools>adb.exe devices
* daemon not running; starting now at tcp:5037
* daemon started successfully
List of devices attached
xxxxxxxxxxxxx unauthorized
```

unauthorizedになっているのはAndroid端末本体側で許可していないから。おそらくこれまでの手順でダイアログが出てきていると思うので許可して下さい。

```:コマンドプロンプト
> D:\Android\SDK\platform-tools>adb.exe devices
List of devices attached
xxxxxxxxxxxxx device
```

上記でOKです！

現在はUSBモードでつながっているのでtcpモードに変更します。

```:コマンドプロンプト
> D:\Android\SDK\platform-tools>adb.exe tcpip 5555
> restarting in TCP mode port: 5555
```

次にWSL2側から接続しますが接続には `Android端末` のIPアドレスで接続します。

```:WSL2
$ adb connect 192.xxx.xxx.xxx
failed to authenticate to 192.xxx.xxx.xxx:5555
```

Andoroid側に許可を求めるダイアログが表示されるはずです。許可してあげてください。

```:WSL2
$ adb connect 192.xxx.xxx.xxx
already connected to 192.xxx.xxx.xxx:5555
```

接続されました！この状態であればUSBケーブルは抜いてもOKです。

```:WSL2
$ flutter devices
1 connected device:

SHV48 (mobile) • 192.xxx.xxx.xxx:5555 • android-arm64 • Android 11 (API 30)
```

こちらも接続できました！

# 参考サイト

- [WSL2とVSCodeで欲しい環境を好きなだけ作る - いろいろ備忘録日記](https://devlights.hatenablog.com/entry/2021/05/11/173126)
    - 素晴らしい内容でした！Flutterに限らずいろんな開発環境の構築が例示されています。よい記事をありがとうございます :bow:

[^terminal]: 今ならWindows Terminalを使うのが正解のようです。Powershellすらまともにつかったことがないのにまた新しいのが…

[^wifi]: https://developer.android.com/studio/command-line/adb#connect-to-a-device-over-wi-fi-android-11+

[^atleast]: "platforms;android-30" は不要かもです。adbコマンド自体は "platform-tools" に入っていました。

[^debugmode]: https://developer.android.com/studio/debug/dev-options?hl=ja