在 iOS 上,Flutter的TextField使用蓝牙键盘时输入无响应

1,014 阅读2分钟

问题

在 iPad 上,使用 Flutter 的 TextField() 控件写的输入框,iPad 上自带的虚拟键盘可以正常输入,但是当通过蓝牙连接了一个键盘外设(物理键盘)时,输入却没有反应,更诡异的是,iPad 自带的备忘录、短信、搜索框等等却能够得到输入的内容。

所以,为什么 Flutter 的 TextField 不能响应蓝牙键盘(物理键盘)的输入?

又去 Google、Github 的海洋浪了一圈,咩都冇~

(Github 的 Flutter/issues 也有人遇到同样的问题,但也没有具体的解决方案,据说 Flutter 官方已经解决了,在 master 分支上,还没上 stable 分支,那再等等看?)

不存在的,老板不允许,产品不允许、主管不允许...

image.png

发现:

在不断的尝试中发现,新建的项目(flutter create XXX_demo),HomePage()中新增一个 TextField() 控件却可以正常输入。

下图就是连接键盘可正常输入的样子

IMG_0406.PNG

所以问题的范围缩小到 iOS 启动 Flutter引擎的是不是少了哪些配置?

带着问题,我去看看了 iOS 原生端启动 Flutter 的时候干了什么

默认的引擎初始化:

@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // storyboard 上绑定的 FlutterViewController
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

我采用的引擎初始化方式:

@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        /// 自己创建 Engine 和 FlutterViewController 绑定
        let flutterEngine : FlutterEngine = FlutterEngine.init(name: "flutter_native", project: nil)
        GeneratedPluginRegistrant.register(with: flutterEngine)
        window = UIWindow.init(frame: UIScreen.main.bounds)
        
        // MainVC 继承于 FlutterViewController
        let flutterController = MainViewController.init(engine: flutterEngine, nibName: nil, bundle: nil)
        window?.rootViewController = flutterController
        window?.makeKeyAndVisible()
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

对比结果:

1.默认方式,使用的是 FlutterViewController 自带的 engine
2.我采用的方式是自己创建了一个 FlutterEngine,并在 FlutterViewController.init 的时候传进去,导致了 FlutterViewController 使用的是外部传入的 engine。

解决方案

经过不断的试错,终于拨开云雾见青天~

方案一:

不需要使用 FlutterViewController 的实例对象,比如:其他需要桥接的内容,自定义 channel 等等操作的话,可以直接使用默认的方式,即 storyboard 绑定 FlutterViewController,代码在上面。

方案二:

需要使用 FlutterViewController,可以不再创建新引擎,使用 FlutterVC 自带的引擎即可.

不一样的点是

1. 创建 flutterVC 不接受外部 engine,否则会覆盖它自带的
2. GeneratedPluginRegistrant 注册的也是 flutterVC 的插件通道
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let flutterController = MainViewController.init()
        GeneratedPluginRegistrant.register(with: flutterController.pluginRegistry())
        
        window = UIWindow.init(frame: UIScreen.main.bounds)
        window?.rootViewController = flutterController
        window?.makeKeyAndVisible()
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)

    }
}

结语

到这里,把我的问题解决了。但是整个过程下来还是有点懵逼的,还有很多疑问,比如说:

  1. 为什么自己在外部创建的 FlutterEngineFlutterViewController里自带的 engine 有什么区别?
  2. 或许不是 engine 的问题,而是引擎启动的时机问题?

以上是我的解决方案,有错误,讲的不对的地方,或许各位大佬有更好的解决方案,欢迎在评论区指出,我会虚心接受! 谢谢~