Flutter desktop embedding を Windows で試す

[この記事を見て](https://medium.com/flutter-community/flutter-on-desktop-a-real-competitor-to-electron-4f049ea6b061) 『すげぇ、Android エミュレータ要らないじゃん』と感動したので、ちょっとやってみた。

# Desktop Embedding for Flutter

https://github.com/google/flutter-desktop-embedding

Flutter で Windows/macOS/Linux 向けのアプリケーションが作れるようになった。
とりあえずよくわからないので、やってみる。

# 環境構築 ~ サンプル実行

まとまった情報が見つけられなかったので、以下を参考に進める。

- https://github.com/google/flutter-desktop-embedding/blob/master/README.md
- https://github.com/google/flutter-desktop-embedding/blob/master/library/README.md
- https://github.com/google/flutter-desktop-embedding/blob/master/Debugging.md
- https://github.com/google/flutter-desktop-embedding/blob/master/example/README.md

## Flutter SDK

まずは、Flutter と Flutter-desktop-embedding を取ってきて置く必要があるらしい。
既に Flutter 環境はあったが、何されるか分からないし汚染されても面倒なので改めて取ってくる。

```powershell
PS> mkdir test; cd test
PS> mkdir sdk; cd sdk
PS> git clone https://github.com/flutter/flutter
PS> git clone https://github.com/google/flutter-desktop-embedding
PS> ls
# drwxrwxrwx - user 30 Jan 22:02 flutter
# drwxrwxrwx - user 30 Jan 22:05 flutter-desktop-embedding
```

## Flutter-desktop-embedding Library の Build

次に、Flutter-desktop-embedding の Library をビルドする必要がある。
Visual Studio Community でのビルドを押しているが、もし仕事で使うなら無料で使えないケースもあるので今回は BuildTools を使ってやってみる。

### Visual Studio BuildTools

ここから、Build Tools for Visual Studio 2017 をダウンロードしてインストールする。

https://visualstudio.microsoft.com/ja/downloads/
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/cf3b609d-ee03-2675-2a96-c62103f36826.png" width="700px" />

インストールが終わると、ビルドツールが `C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\` 以下に配置されるので、必要な場所にパスを通しておく。

- ___C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin___
- ___C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build___

パスが通っていれば、以下が動くはず。

```powershell
PS> MSBuild.exe /version
# .NET Framework 向け Microsoft (R) Build Engine バージョン 15.9.21+g9802d43bc3
# Copyright (C) Microsoft Corporation.All rights reserved.
# 
# 15.9.21.664

PS> vcvars64.bat
# **********************************************************************
# ** Visual Studio 2017 Developer Command Prompt v15.0
# ** Copyright (c) 2017 Microsoft Corporation
# **********************************************************************
# [vcvarsall.bat] Environment initialized for: 'x64'
```

**● Windows SDK**

実際にビルドしようとすると、以下のようなエラーが出ることがある。
![2019-01-30_23h01_36.png](https://qiita-image-store.s3.amazonaws.com/0/20198/71b4d57c-6ee3-8d0e-18d2-1db396169e30.png)
Windows SDK の Windows 10 SDK Version 1803  が足りないということなので、以下からダウンロードし、インストールする必要がある。

https://developer.microsoft.com/ja-jp/windows/downloads/sdk-archive
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/884f7ff8-52bc-0288-44ca-3801d98daf44.png" width="700px" />
インストールが完了したら、ビルドを行う。

```powershell
PS> cd sdk\flutter-desktop-embedding\library\windows
PS> MSBuild.exe '.\Flutter Windows Embedder.sln'
```

正常にビルドが完了すると、`bin\x64\Debug Dynamic Library\GLFW Library` 以下に `flutter_embedder.dll` や `flutter_engine.dll` が生成されている。

## サンプル実行

ということで、ここで一度サンプルを実行してみる。

```powershell
PS> cd sdk\flutter-desktop-embedding\example\windows_fde
PS> MSBuild.exe '.\Example Embedder.sln'
```

正常にビルドが完了すると、`bin\x64\Debug Dynamic Library\GLFW Example` 以下に `GLFW Example.exe` が生成されるので、起動してみる ( 起動は Working Directory を `windows_fde` にして実行する必要がある )。

```powershell
PS> & '.\bin\x64\Debug Dynamic Library\GLFW Example\GLFW Example.exe'

# flutter: Observatory listening on http://127.0.0.1:57524/
```

起動はとても早い。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/57a0ef34-94d1-79eb-7acf-52f8138c854e.png" width="500px" />

メモリ使用量もとても低く、Android エミュレータとは比べ物にならない。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/21a95d4f-9d29-6591-87d4-e6e1e7ef97bb.png" width="700px" />

ただ、リサイズに関しては、元記事のように滑らかにはならず、一瞬黒い画面になる。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/86f72e9d-8415-67e5-9d39-eb50b9b558d9.gif" width="600px" />


# デバッグ

デバッグ環境も構築する。
今回は、 VS Code から attach する。
Dart のソースコードは `flutter-desktop-embedding\example\lib` 以下にある。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/eacfe0f5-5b99-157c-804d-a1c6bf799f80.png" width="500px" />

まずは、Flutter Plugin を導入する。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/0691ccb2-b3ca-df2c-bd1c-ad8c1fd61c66.png" width="500px" />

次に、Debug 設定を行う。
VS Code のサイドバーで Debug を選択して、`Add Configuration` で雛形を生成する。
![2019-01-31_06h19_58.png](https://qiita-image-store.s3.amazonaws.com/0/20198/cf84e4b4-2c77-7f35-0f1d-540f6ef50b74.png)

出来た雛形を、以下のように書き換える

```json
  {
    "name": "Flutter",
    "request": "attach",
    "deviceId": "flutter-tester",
    "observatoryUri": "http://127.0.0.1:<<さっき起動した時の Port>>/",
    "type": "dart"
  }
```

![flutter.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/730e803d-e4e0-3342-80d7-e16a66eab9a8.gif)

# リリース

リリースビルドもやってみる。

```powershell
PS> cd sdk\flutter-desktop-embedding\example\windows_fde
PS> MSBuild.exe '.\Example Embedder.sln' /p:Configuration="Release Dynamic Library"
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/c14a8c76-c385-ead0-f4c4-8905483da24f.png)

# 中身を見てみる

そもそも alpha ですらないので、詳しく調べてもしょうがない気もするが、ちょっと調べてみた。

## Embedder

これが、Flutter を Desktop Application に埋め込む機能を持つライブラリ。`flutter-desktop-embedding\library` 以下にソースがある。
Pre-Build なバイナリが提供されていないのは、ビルド生成物と flutter_assets のバージョンが合わないと動かなくなるかららしい。

Windows 版のビルド過程を見ていくと、

**1. PreBuild**

以下 Script を順次実行していく。

- ___library\windows\scripts\update\_flutter\_engine.bat___
    - Flutter Engine ( flutter_engine.dll ) の取得・更新
- ___library\windows\scripts\get\_engine\_artifacts.bat___
    - Flutter Project から ICU Data ( icudtl.dat ) をコピー
- ___library\windows\scripts\get\_GLFW.bat___
    - `http://github.com/glfw/glfw` から Dll ( glfw3.dll ) とヘッダファイルを取得
- ___library\windows\scripts\build\_jsonlib.bat___
    - `https://github.com/open-source-parsers/jsoncpp` からソースファイルとヘッダファイル取得、ビルド

これらバッチファイルの実態は、`flutter-desktop-embedding\tools` 以下のバッチファイルや `flutter-desktop-embedding\tools\dart_tools` 以下の Dart ファイルを呼び出しているだけ。

**2. Build**

`flutter-desktop-embedding\library\include` と PreBuild で取得したヘッダファイルを Include し、`flutter-desktop-embedding\library\common` にあるソースをビルドして `flutter_embedder.dll` を生成する。

**3. PreLink**

ビルド後に、1. で取得した `flutter_engine.dll` を `flutter_embedder.dll` と同じ出力先にコピー。

これで、Embedder ライブラリ作成は完了。
以降はこれを使い回すこともできるだろうが、flutter_assets のバージョンが上がったらそれに合わせてリビルドする必要はありそうだ。

## Flutter Project

`flutter-desktop-embedding\example` が Flutter プロジェクトの構成をしている。
以下が標準的な Flutter プロジェクトに含まれるフォルダ・ファイルとなる。

```powershell
# .
# └── example
#     ├── android\
#     ├── build\
#     ├── fonts\
#     ├── ios\
#     ├── lib\
#     ├── test\
#     ├── .gitignore
#     ├── .metadata
#     ├── .packages
#     ├── pubspec.lock
#     ├── pubspec.yaml
#     └── README.md
```

`flutter-desktop-embedding\example` では、そこに `linux_fde` `macos_fde` `windows_fde` が新たに追加されている。 ( `_fde` [ `f`lutter `d`esktop `e`mbedding  ] が付くのは、本家 Flutter がこれら Platform に対応した際、名前がバッティングする可能性がある為 )

Dart の設定ファイルである `pubspec.yaml` を見てみると、

```yaml:pubspec.yaml
name: example_flutter
description: An example project for flutter-desktop-embedding.

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0

  # Desktop embedder plugins.
  # Note: In an actual project, these paths would include the
  # path from your application to the flutter_desktop_embedding
  # checkout.
  color_panel:
    path: ../plugins/color_panel
  file_chooser:
    path: ../plugins/file_chooser
  menubar:
    path: ../plugins/menubar

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

  fonts:
    - family: Roboto
      fonts:
        - asset: fonts/Roboto/Roboto-Thin.ttf
          weight: 100
        - asset: fonts/Roboto/Roboto-Light.ttf
          weight: 300
        - asset: fonts/Roboto/Roboto-Regular.ttf
          weight: 400
        - asset: fonts/Roboto/Roboto-Medium.ttf
          weight: 500
        - asset: fonts/Roboto/Roboto-Bold.ttf
          weight: 700
        - asset: fonts/Roboto/Roboto-Black.ttf
          weight: 900
```

一つ上の階層にある `flutter-desktop-embedding\plugins` と、同階層にある `flutter-desktop-embedding\example\fonts` を参照している。

## XXXX_fde Project

実際に Emmbed するプロジェクトである windows_fde がビルド時に何をしているかを見ていくと、

**1. PreBuild**

` flutter build bundle` で、`flutter-desktop-embedding\example\build\` 以下 assets を生成

**2. Build**

Embeddr が生成した Dll をリンクし、`flutter-desktop-embedding\example\windows_fde\flutter_embedder_example.cpp` をビルドして `GLFW Example.exe` を生成する

**3. PreLink, PostBuild**

`GLFW Example.exe` の隣に `flutter_embedder.dll`, `flutter_engine.dll` をコピーする。
これで、ビルドは完了。

ちなみに `flutter_embedder_example.cpp` の中身を見てみると、

```cpp:flutter_embedder_example.cpp
...

#include "flutter_desktop_embedding/glfw/flutter_window_controller.h"

int main(int argc, char **argv) {
  // TODO: Make paths relative to the executable so it can be run from anywhere.
  std::string assets_path = "..\\build\\flutter_assets";
  std::string icu_data_path = "..\\..\\library\\windows\\dependencies\\engine\\icudtl.dat";
...

  flutter_desktop_embedding::FlutterWindowController flutter_controller(icu_data_path);

  // Start the engine.
  if (!flutter_controller.CreateWindow(640, 480, assets_path, arguments)) {
    return EXIT_FAILURE;
  }

  // Run until the window is closed.
  flutter_controller.RunEventLoop();
  return EXIT_SUCCESS;
}
```

とてもシンプルで、flutter_controller を icu_data 付きで初期化して、`CreateWindow` に flutter_assets を渡して Application を表示させている。
asset_path が相対パスで指定されていて、起動した時に asset ファイルを読み込んでるのかと思ったが、書き換えても内容が変更しないのはちょっと不思議。

# 感想

とりあえず、GLFW ( OpenGL ) を使って GUI 作ってるってのは分かった。

SAVE ボタンを押すとエラーが起きたり、突然の死も多く、まだまだ安定しているとは言えない。
もう少し様子見。