user icon

FlutterのWindowsアプリでIMEの切り替え

FlutterのWindowsアプリで、TextFieldフォーカス時にIMEをオン/オフ切り替える機能を実装してみました。
最初、TextFieldのTextInputTypeを使ってキーボードをphoneemailAddressに指定すればIMEが切り替わるのでは、と思い試しましたが、切り替わりませんでした。
プラグインでIMEを切り替えられるものを探しましたが見つからなかったので、以下を参考にwin32 APIを呼び出す方法で実装しました。
https://docs.flutter.dev/development/platform-integration/platform-channels#step-5-add-a-windows-platform-specific-implementation

上記に書かれてるように、まずwindows/runner/flutter_window.cpp#include "flutter_window.h"の後に以下を追記します。

#include <flutter/event_channel.h>
#include <flutter/event_sink.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>

#include <memory>

dllを呼び出すため以下をflutter_window.cppに記述します。

#pragma comment(lib, "imm32.lib")
#pragma comment(lib, "user32.lib")

FlutterWindow::OnCreate メソッドを編集し、チャンネル名 hoge.flutter.dev/imeに結びついた flutter::MethodChannelを作成します。

bool FlutterWindow::OnCreate() {
  // ...

  flutter::MethodChannel<> channel(
      flutter_controller_->engine()->messenger(), "hoge.flutter.dev/ime",
      &flutter::StandardMethodCodec::GetInstance());
  channel.SetMethodCallHandler(
      [](const flutter::MethodCall<>& call,
         std::unique_ptr<flutter::MethodResult<>> result) {

            HWND hwnd = GetForegroundWindow();
            HIMC himc = ImmGetContext(hwnd);
            if (call.method_name() == "imeOn") {
                ImmSetOpenStatus(himc, true);
            } else {
                ImmSetOpenStatus(himc, false);
            }
            ImmReleaseContext(hwnd, himc);
            result->Success();

      });
  return true;
}

次にFlutter側でチャネルを介してメッセージを送信するようにします。
以下のWidgetを作成します。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class ToggleIme extends StatelessWidget {
  final Widget child;
  final bool enable;
  const ToggleIme({super.key, required  this.child, required this.enable });
  static const platform = MethodChannel('hoge.flutter.dev/ime');

  @override
  Widget build(BuildContext context) {
    return Focus(
      skipTraversal: true,
      child: child,
      onFocusChange: (hasFocus) {
        if (hasFocus) {
          if (enable) {
            platform.invokeMethod('imeOn');
          } else {
            platform.invokeMethod('imeOff');
          }
        }
      } ,
    );
  }
}

TextFieldを以下のような感じでラップします。

ToggleIme(
    enable: false,
    child: TextField(),
)

これでTextFieldフォーカス時にIMEがオフになるようになりました。

Facebooktwitterlinkedintumblrmail

タグ:

名前
E-mail
URL
コメント

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)