Flutter

## 1.1 環境構築
### 1.1.1 手順
(1) プロジェクト用のフォルダを作成する 
(2) Dockerfile , docker-compose.yml, .envを作成する
(3) Dockerfileからイメージを作成する
(4) イメージからコンテナを作成し、立ち上げる
(5) 起動したコンテナに入る
(6) Flutterアプリを作成する
(7) サーバーを立ち上げる

### 1.1.2 Dockerfileを作成する
~~~Dockerfile:Dockerfile
# build時に使用するARGを定義している部分
ARG ubuntu_version
ARG timezone
ARG web_server_port
ARG app_code_path_container

# https://hub.docker.com/_/ubuntu/
FROM ubuntu:${ubuntu_version}

ENV DEBIAN_FRONTEND=noninteractive
ENV LC_ALL=ja_JP.UTF-8
ENV LC_CTYPE=ja_JP.UTF-8
ENV LANGUAGE=ja_JP:jp
ENV TZ $timezone
ENV WEB_SERVER_PORT $web_server_port
ENV APP_CODE_PATH_CONTAINER $app_code_path_container

# Ubuntu base setting (locale と timezone と デバッグによく使用するものを入れる)
RUN apt-get update \
    && apt-get -y -q install \
    # Lang ja
    language-pack-ja-base language-pack-ja apt-transport-https \
    # devtool
    vim netcat vim git curl wget zip unzip make sudo gcc libc-dev clang net-tools \
    xserver-xorg pkg-config libgtk-3-dev cmake ninja-build gnupg software-properties-common \
    && locale-gen ja_JP.UTF-8 \
    && localedef -f UTF-8 -i ja_JP ja_JP.utf8 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && echo "${TZ}" > /etc/timezone \
    && rm /etc/localtime \
    && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    && dpkg-reconfigure -f noninteractive tzdata

# requisites software（Flutter関連で使用するものを入れる）
RUN echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \
    && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
    && add-apt-repository ppa:maarten-fonville/android-studio
RUN apt-get update && \
    apt-get -y -q install \
    xz-utils libglu1-mesa openjdk-8-jdk google-chrome-stable android-studio \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# AndroidをビルドできるようにAndroidSDKを入れる
WORKDIR /usr/local
RUN mkdir -p Android/sdk
ENV ANDROID_SDK_ROOT /usr/local/Android/sdk
RUN mkdir -p .android && touch .android/repositories.cfg
RUN wget -O sdk-tools.zip https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
RUN unzip sdk-tools.zip && rm sdk-tools.zip
RUN mv tools Android/sdk/tools
RUN cd Android/sdk/tools/bin && yes | ./sdkmanager --licenses
RUN cd Android/sdk/tools/bin && ./sdkmanager "build-tools;31.0.0" "platform-tools" "platforms;android-31" "sources;android-31"
ENV PATH "$PATH:/usr/local/Android/sdk/platform-tools"

# Dartのインストール
RUN sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
RUN apt-get update && \
    apt-get -y -q install \
    dart \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Download Flutter SDK
RUN git clone https://github.com/flutter/flutter.git
ENV PATH "$PATH:/usr/local/flutter/bin"

# Flutter Webが使用できるようにする
RUN flutter config --enable-web
# Flutterの診断
RUN flutter doctor

EXPOSE ${WEB_SERVER_PORT}

# 参考サイト:
# https://qiita.com/__yuporon__/items/f06a6361dbc395bf75a9
~~~

### 1.1.3 docker-compose.ymlを作成する
~~~docker-compose.yml
version: '3.7'
# 今回のdocker-compose.ymlでは意味ないがlogの設定を別にして使いまわししやすくしている
x-logging:
  &default-logging
  options:
    max-size: '12m'
    max-file: '5'
  driver: json-file

services:
  flutter:
    container_name: flutter
    build:
      context: .
      args:
        # Dockerfile内でARGしている変数に.envに定義した値を代入。
        - ubuntu_version=${UBUNTU_VERSION}
        - timezone=${TIMEZONE}
        - web_server_port=${WEB_SERVER_PORT}
        - app_code_path_container=${APP_CODE_PATH_CONTAINER}
    tty: true
    env_file: .env
    volumes:
      - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}${APP_CODE_CONTAINER_FLAG}
    logging: *default-logging
    # web-serverとして動作させるためにportを指定。
    ports:
      - "$WEB_SERVER_PORT:$WEB_SERVER_PORT"
~~~

### 1.1.4 envを作成する
~~~.env
# ここの名前でアプリ名が変わるので自分のアプリ名を設定してください
# ここでは、Muscle_Masterとする
APP_NAME=Muscle_Master

APP_CODE_PATH_HOST=./src
APP_CODE_PATH_CONTAINER=/usr/local/workspace
APP_CODE_CONTAINER_FLAG=:cached

TIMEZONE=Asia/Tokyo
UBUNTU_VERSION=20.04
WEB_SERVER_PORT=8888
~~~

### 1.1.5 Dockerfileからイメージを作成する
~~~
docker-compose build
~~~

### 1.1.6 イメージからコンテナを作成し、起動する
~~~
docker-compose up -d
~~~

### 1.1.7 起動したコンテナに入る
~~~
docker exec -it flutter bash
~~~

### 1.1.8 フラッターアプリを作成する
~~~
cd workspace
flutter create .
~~~

### 1.1.9 サーバーを立ち上げる
~~~
flutter run -d web-server --web-port=${WEB_SERVER_PORT} --web-hostname 0.0.0.0
~~~

### 1.1.10  エラー
~~~
 => ERROR [12/17] RUN cd Android/sdk/tools/bin && ./sdkmanager "build-tools;29.0.2" "patcher;v4" "platform-tools"  7.0s
------
 > [12/17] RUN cd Android/sdk/tools/bin && ./sdkmanager "build-tools;29.0.2" "patcher;v4" "platform-tools" "platforms;android-29" "sources;android-29":
Warning: File /root/.android/repositories.cfg could not be loaded.
Warning: Failed to find package patcher;v4                                      .
#15 7.000 [===                                    ] 10% Computing updates...
------
executor failed running [/bin/sh -c cd Android/sdk/tools/bin && ./sdkmanager "build-tools;29.0.2" "patcher;v4" "platform-tools" "platforms;android-29" "sources;android-29"]: exit code: 1
ERROR: Service 'flutter' failed to build : Build failed
~~~

#### (a) 原因
~~~
RUN cd Android/sdk/tools/bin && ./sdkmanager "build-tools;29.0.2" "platform-tools" "platforms;android-29" "sources;android-29"
~~~
上記の行では、build-tools がバージョン 29.0.2 で指定されている。このバージョンが存在しないか古い場合、エラーが発生する。

#### (b) 解決法
Dockerfile内のbuild-toolsのバージョンを29.0.2から31.0.0にした。
~~~
RUN cd Android/sdk/tools/bin && ./sdkmanager "build-tools;31.0.0" "platform-tools" "platforms;android-31" "sources;android-31"
~~~

### 1.1.11 エラー

~~~
Desktop/muscle_master# docker-compose run flutter flutter create muscle_master

Creating muscle_master_flutter_run ... done
Error response from daemon: error while creating mount source path '/run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu/77fec1a8cf2153eac456a551bceccceae3db92f0d9a52610ca02f1b5316b56b7': mkdir /run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu/77fec1a8cf2153eac456a551bceccceae3db92f0d9a52610ca02f1b5316b56b7: file exists
ERROR: 1
~~~

#### (a) 原因
Dockerがコンテナ内で指定されたディレクトリをマウントしようとした際に、既にそのディレクトリが他の場所でマウントされているため。具体的には、Windows Subsystem for Linux (WSL)上でDockerが使用されており、マウントポイントが競合していた。

#### (b) 解決方法

(1) ディレクトリが他のファイルシステムにマウントされているかどうか確認する
~~~
mount | grep 77fec1a8cf2153eac456a551bceccceae3db92f0d9a52610ca02f1b5316b56b7
~~~

~~~
/Desktop/muscle_master# mount | grep 77fec1a8cf2153eac456a551bceccceae3db92f0d9a52610ca02f1b5316b56b7
drvfs on /mnt/wsl/docker-desktop-bind-mounts/Ubuntu/77fec1a8cf2153eac456a551bceccceae3db92f0d9a52610ca02f1b5316b56b7 type 9p (rw,noatime,dirsync,aname=drvfs;path=C:\;uid=0;gid=0;symlinkroot=/mnt/,mmap,access=client,msize=262144,trans=virtio)
~~~


(2) マウントが確認されれば、umountコマンドを使用してアンマウント
~~~
sudo umount /mnt/wsl/docker-desktop-bind-mounts/Ubuntu/77fec1a8cf2153eac456a551bceccceae3db92f0d9a52610ca02f1b5316b56b7
~~~





 ## ※ dockerで作成されたファイルが自分のPCに反映される仕組み

~~~docker-compose.yml
  volumes:
      - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}${APP_CODE_CONTAINER_FLAG}
~~~

~~~.env
APP_CODE_PATH_HOST=./src
APP_CODE_PATH_CONTAINER=/usr/local/workspace
APP_CODE_CONTAINER_FLAG=:cached
~~~

DockerでホストのPCとコンテナ間でファイルが共有される原理は、Dockerのボリューム機能を利用している。ボリュームを使用すると、ホストとコンテナ間でデータを共有でき、ホスト上のデータが変更されるとコンテナ内のデータも同期される。

~~~yaml:docker-compose.yml
  volumes:
      - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}${APP_CODE_CONTAINER_FLAG}
~~~
この部分はボリュームを定義している。ここで使用されている環境変数は以下の通りである。

- APP_CODE_PATH_HOST: ホストマシン上のディレクトリのパス
- APP_CODE_PATH＿CONTAINER: コンテナ内のディレクトリのパス
- APP_CODE_CONTAIER_FLAG: ボリュームのオプション(ここでは:cached)

この定義により、ホストの`${APP_CODE_PATH_HOST}`ディレクトリと
コンテナの`$APP_CODE_PATH_CONTAINER}`ディレクトリが同期される。
APP_CODE_CONTAINER_FLAGに:cachedが指定されている場合、ボリュームがキャッシュされたボリュームとしてマウントされる。これにより、ホスト上での変更が高速に反映され、パフォーマンスが向上する。

与えられた.envファイルには次のような変数が定義されている。
~~~:.env
APP_CODE_PATH_HOST=./src
APP_CODE_PATH_CONTAINER=/usr/local/workspace
APP_CODE_CONTAINER_FLAG=:cached
~~~

これに基づいて、ボリュームの定義は以下のようになる。
~~~docker-compose.yml
volumes:
  - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}${APP_CODE_CONTAINER_FLAG}
~~~

これにより、ホストの./srcディレクトリがコンテナの/usr/local/workspaceディレクトリと共有される。また、:cachedオプションが指定されているため、ボリュームは「キャッシュされたボリューム」としてマウントされる。このボリュームにより、ホスト側での変更が高速にコンテナに反映され、パフォーマンスが向上する。

つまり、ホスト上の./srcディレクトリの変更がコンテナ内の'/usr/local/workspaceディレクトリに同期され、両者が連携して作業できるようになる。



## 参考文献
[1] DockerでFlutterの環境構築メモ
　https://qiita.com/yuporon/items/f06a6361dbc395bf75a9
[2] Flutterフラッター 入門講座
　https://www.youtube.com/playlist?list=PLY1cxwSQf6nz6zo2Y_UJlDjGOpASAO4hd