FlutterからWindowsネイティブAPI(Win32API)を呼ぶ

一例として、ウィンドウ(HWND)を画面の中央に移動する機能を実装します。

# Dart側の実装

ボタンを作成します。

```main.dart
ElevatedButton(
  child: const Text("ウィンドウの位置変更"),
  onPressed: () async {
    const platform = MethodChannel("domain/appname");
    await platform.invokeMethod("moveWnd");
  },
),
```

# Windowsネイティブ側の実装

flutter_window.hの `private:`の下にメソッドの宣言を追加します。
C++はメソッドの宣言と実装をそれぞれ別のファイルで行う必要があります。

```windows/runner/flutter_window.h
void setMethodChannel(flutter::FlutterEngine *engine);
```

flutter_window.cppの末尾にメソッドを実装します。
`"domain/appname"`と`"moveWnd"`はDart側の実装と繋がっているため、文字列を変更する場合はDart側も変更してください。

```windows/runner/flutter_window.cpp
#include "flutter/method_channel.h"
#include "flutter/standard_method_codec.h"

#define GetMonitorRect(rc)  SystemParametersInfo(SPI_GETWORKAREA,0,rc,0)

void FlutterWindow::setMethodChannel(flutter::FlutterEngine *engine) {
  const std::string test_channel("domain/appname");
  const flutter::StandardMethodCodec& codec = flutter::StandardMethodCodec::GetInstance();

  flutter::MethodChannel method_channel_(engine->messenger(), test_channel, &codec);
  method_channel_.SetMethodCallHandler([&](const auto& call, auto result) {
    std::string name = call.method_name();
    if (name.compare("moveWnd") == 0) {
      RECT monitorRect, wndRect;
      GetMonitorRect(&monitorRect);
      GetWindowRect(contentWnd, &wndRect);
      INT monitorHeight = (monitorRect.bottom - monitorRect.top);
      INT wndHeight = (wndRect.bottom - wndRect.top);
      INT monitorWidth= (monitorRect.right - monitorRect.left);
      INT wndWidth = (wndRect.right - wndRect.left);
      INT pointY = (monitorHeight - wndHeight) / 2;
      INT pointX = (monitorWidth - wndWidth) / 2;
      SetWindowPos(contentWnd, NULL, pointX, pointY, 0, 0,
        (SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER));
      result->Success();
    } else {
	    std::cout << "No register method. name=" << name << std::endl;
    }
  });
}
```

flutter_window.cppのOnCreateメソッドに1行追加します。

```windows/runner/flutter_window.cpp
bool FlutterWindow::OnCreate() {
  ...
  setMethodChannel(flutter_controller_->engine());
}
```

今回のWin32APIにはHWNDインスタンスが必要なので、win32_window.h/cppも書き換えます。
HWNDを必要としないWin32APIを使う場合は以下実装は不要です。


win32_window.hの`protected:`の下で変数を宣言します。

```windows/runner/win32_window.h
HWND contentWnd;
```

119行目付近を書き換えて、HWNDを変数で保持します。

```windows/runner/win32_window.cpp
contentWnd = CreateWindow(
    window_class, title.c_str(), WS_VISIBLE,
    Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
    Scale(size.width, scale_factor), Scale(size.height, scale_factor),
    nullptr, nullptr, GetModuleHandle(nullptr), this);

if (!contentWnd) {
  return false;
}
```

以上で実装は終わりです。
アプリを実行し直してボタンをタップすれば、ウィンドウが画面の中央に移動します。

:::note warn
警告
ネイティブ側の実装はホットリロードで反映されないため、必ずアプリを実行し直してください。
:::

# 環境

```
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.5.2, on Microsoft Windows [Version 10.0.19043.1288], locale ja-JP)
[√] Visual Studio - develop for Windows (Visual Studio Community 2019 16.11.5)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[√] Chrome - develop for the web
[√] Android Studio (version 4.2)
```

Flutterバージョン2.5.2で確認しています。
バージョン1系は実装が異なるようなので、以下記事を参考にしてください。

https://qiita.com/br_branch/items/746ba34cc2d5780ee8db#%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E9%80%A3%E6%90%BA%E3%81%99%E3%82%8B

# 追記

実装してから気づきましたが、ウィンドウを移動するだけならMethodChannelは必要ありませんでした。
win32パッケージを導入することで、Dart側でSetWindowPosメソッドを使えます。

https://pub.dev/packages/win32

# 参考文献

https://www.jianshu.com/p/cc0ce5784922

https://blog.goo.ne.jp/masaki_goo_2006/e/b0287481d97f9602efce0cc245876b24