DartのみでQiita夏祭り2020の記事一覧ページを作った

この記事は Qiita 夏祭り2020のテーマ、[「今〇〇（言語）のみで△△ (アプリ) を作るとしたら」](https://qiita.com/official-events/5d181aadf5db26b73d33)の投稿です。

## 作ったもの
- [デモサイト (Webアプリ版)](https://nskhei.github.io/flutter-qiita-viewer/)
- [ソースコード](https://github.com/nskhei/flutter-qiita-viewer)

Qiita夏祭り関連の記事一覧が見れます。
__Flutter製なので、スマホアプリや、macOSアプリにもコンパイルできます。__
参考までに以下は mac版のスクショです。
![macos_screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266232/4a31c32a-5542-6b84-0112-40725ea4b047.png)

## Dart とは
Googleが開発した__オブジェクト指向型__の言語です。

学習コストがとても低いので、プログラミング初心者にもオススメできます。
言語仕様は「Java と Javascript を足したような言語」といった表現をよく見ます。


## Flutter とは
Google が中心になって開発している、クロスプラットフォームの **UI フレームワーク**です。
コードはプラットフォーム毎のマシンコードにコンパイルされるので、高速に動作します。

![platforms.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266232/e22733ec-ecd5-86e5-b838-d9f099e93ee7.png)


## 環境構築
既に日本語記事がたくさん出てますが、一応簡単に書いておきます。

Flutter のインストール方法には

- __公式サイトからzipファイルをダウンロード__する方法
- __リポジトリから直接 `git clone`__ する方法

の２種類があります。
今回は、後者で解説していきます。

### Flutter本体のインストール

#### インストール先を決める

どこでもいいのですが、今回は公式に習って`~/development`フォルダにインストールすることにします。

```bash
# フォルダ作成
$ mkdir ~/development

# 移動
$ cd ~/development
```

#### `git clone`する

リポジトリからFlutter 本体をダウンロードします。
Dart の SDK もこの中に入っています。

```bash

$ git clone https://github.com/flutter/flutter.git
```

#### パスを通す
最後に`~/.bash_profile`を開いて、Flutter をインストールしたパスを追記します。

```
export PATH=$PATH:$HOME/development/flutter/bin
```

さて、これで`flutter`コマンドが使えるようになったはずです。
ターミナルを再起動して、試しに`flutter --version`コマンドを実行してみます。
以下のように、バージョン情報が表示されればOKです。

```bash
$ flutter --version
Flutter 1.20.0-7.1.pre • channel dev • https://github.com/flutter/flutter.git
Framework • revision 7736f3bc90 (2 days ago) • 2020-07-10 16:33:05 -0700
Engine • revision d48085141c
Tools • Dart 2.9.0 (build 2.9.0-21.2.beta)
```

### その他必要なツールのインストール
`flutter doctor`コマンドを実行すると、現在の環境に足りてないものを教えてくれます。
表示される指示に従って、インストールしてください。

ちなみに、Web 版は Xcode や AndroidStudio を入れなくても実行できるそうです。


## Web とmacOS 開発の有効化

2020年7月現在、Web開発はβ 版、macOS 開発はα 版になっています。
これらの開発を行うには、__少し追加の設定が必要__になります。


### dev チャンネルに切り替え
最初は stable チャンネルにいると思いますので、dev チャンネルに切り替えます。

```bash
# チャンネルの切り替え
$ flutter channel dev

# 最新版にしておく
$ flutter upgrade
```

### 有効化

下記を実行します。

```bash
flutter config --enable-web
flutter config --enable-macos-desktop
```

### 利用可能デバイスの確認

`flutter devices`コマンドで利用できるデバイスを確認してみます。
以下のように、Chrome と macOS が表示されればOKです。

```bash
$ flutter devices
3 connected devices:

macOS (desktop)  • macos      • darwin-x64     • Mac OS X 10.15.5 19F101
Web Server (web) • web-server • web-javascript • Flutter Tools
Chrome (web)     • chrome     • web-javascript • Google Chrome 83.0.4103.116
```

## 実装
ソースコードは[こちら](https://github.com/nskhei/flutter-qiita-viewer)で公開してます。
以下は、__webとmacOS対応するためのポイント__です。

### プロジェクトの作成
新規プロジェクトを作るには、`flutter create` コマンドを使います。

```bash
$ flutter create qiita_viewer
```

web と macOS の有効化を行っている場合は、
それぞれ`web`ディレクトリと`macos`ディレクトリが作られます。
![web_and_macos_folder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266232/eacbbf97-5c2e-2271-4118-28509b1eee6e.png)

### 起動
#### web版

```bash
$ flutter run -d chrome
```

#### macOS版
```bash
$ flutter run -d macOS
```

両方とも`--release`フラグをつけることで、リリースモードで実行できます。

実際に動かしてみると、web 版はリリースモードでも少し動きがもっさりします。
対してmacOS 版は想像以上にサクサク動きます。

### プラットフォームによる分岐

```dart
// webかどうかの判定
if (kIsWeb){}

// macかどうかの判定
if (Platform.isMacOS){}
```

### レスポンシブ対応

今回作成したアプリでは、メインコンテンツが横に伸びすぎると見栄えが悪いので、`ConstrainedBox`を使って、最大幅を設定した上で、センタリングしています。

```dart
return Center(
  child: ConstrainedBox(
    constraints: BoxConstraints(maxWidth: 1000),
    child: // メインコンテンツ
  )
);
```

また、画面サイズによってコンポーネントを出し分けたい場合は、以下のように`build()`内で分岐させます。
~~web とmacOS 版では、~~`MediaQuery.of()`を使って画面サイズなどを取得しているWidgetは、ブラウザのサイズが変わるたびに`build()`が呼ばれるようです。

```dart
@override
Widget build(BuildContext context) {
  if (MediaQuery.of(context).size.width < 768){
    return SmallLayout();
  }
  return NormalLayout();
}
```

### macOSでの通信エラー
初期状態では、macOSだけ通信エラーが発生します。

`macos/Runner/`の中にある、`DebugProfile.entitlements`と`Release.entitlements`ファイルに__ネットワーク権限を追加__することで解決します。([参考](https://flutter.dev/desktop#entitlements-and-the-app-sandbox))

```macos/Runner/*.entitilements
<dict>
  // これを追加
  <key>com.apple.security.network.client</key>
  <true/>
</dict>
```
## モバイル版のビルド&実機インストール
### iOS

```bash
$ flutter build ios

# 実機を繋いだ状態で
$ flutter install
```

### Android

```bash
$ flutter build apk

# 実機を繋いだ状態で
$ flutter install
```




## Web版のビルド&デプロイ

以下のコマンドでリリースビルドができます。

```bash
$ flutter build web
```

__ビルド結果は`build/web/`に出力される__ので、この中身をサーバーに配置すれば公開できます。

今回はデモページをGithub Pagesで公開しているので、`docs`フォルダの中にコピーしてます。
コマンドで言うと以下のようになります。

```
$ cp -r build/web/* docs/
```

## まとめ ・ 感想
* __iOS 、Android 、ブラウザ、macOS で同じアプリケーションが動く__
* モバイル版はすでに安定している
* Web 版はまだちょっと重い
* macOS 版は割とサクサクだが、対応パッケージが少ない (今回はWebViewが使えなかった)