Flutter Webview - 在 Android 上捕获 touchstart 事件

Flutter Webview - trapping touchstart events on Android

在 Github 上发布的这个 issue 似乎暗示 touch events 在 Android 实现中被故意抑制,表面上是为了能够检测 longpresses copy/paste 的目的。

我自己的应用程序绝对需要能够捕获触摸事件 - 长按文本选择没有任何相关性。我想我会通过使用 Flutter webview implementation code 的本地副本,然后抑制 longpress 过滤器来做到这一点。 webview_android.dart中的相关代码为

We prevent text selection by intercepting the long press event. This is a temporary stop gap due to issues with text selection on Android: https://github.com/flutter/flutter/issues/24585 - the text selection dialog is not responding to touch events. https://github.com/flutter/flutter/issues/24584 - the text selection handles are not showing. TODO(amirh): remove this when the issues above are fixed. onLongPress: () {},

为此,我下载了 Flutter 源代码,并确保 webview_flutter 在我的 Flutter 项目根文件夹下两层的文件夹中可用。将pubspec.yaml修改为

  webview_flutter:
    path: ../../webview_flutter

和 运行 flutter clean && flutter run APK 已构建并安装在我的测试设备上。然而,它随后报告

Setting up FlutterEngine. D/FlutterActivityAndFragmentDelegate(24999): No preferred FlutterEngine was provided. Creating a new FlutterEngine for this FlutterFragment. W/FlutterEngine(24999): Tried to automatically register plugins with FlutterEngine (io.flutter.embedding.engine.FlutterEngine@9480b1a) but could not find and invoke the GeneratedPluginRegistrant.

我不明白这条消息。如果有人能告诉我 how/whether 它可以修复,我将不胜感激。

我现在偶然发现了解决 Flutter Webview 问题的方法 "no touch events in Android"。我觉得与其删除我的问题,不如将其与我找到的解决方案一起留在这里对其他人更有用。

我开始打算自己在 Flutter 中捕获 Touch 事件,然后通过 evaluateJavascript 将它们传输到 WebView。为此,我更改了 Widget 结构,以便将 WebView 包装在 Listener 中。我的整个 Widget 构建器代码如下所示。

@override Widget build(BuildContext context) 
{
 SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);   

 return WidgetsApp
 (
  onGenerateRoute:makeWebView,
  color:Color.fromRGBO(0,128,255,1.0)
 );
} 


Route makeWebView(RouteSettings settings)
{
 return new PageRouteBuilder
 (
  pageBuilder:(BuildContext context,Animation<double> animation,Animation<double> 
  secondaryAnimation)
  {
   return SafeArea
   (
    child:Listener
    (
     child: SizedBox.expand
     (
      child:WebView
      (
       debuggingEnabled:true, 
       initialUrl:'',
       javascriptMode:JavascriptMode.unrestricted,
       onWebViewCreated:registerController,
       javascriptChannels:Set.from
       ([JavascriptChannel(name:'JSBridge',onMessageReceived:handleMessage)]),
      ),
     ),
    )
   );
  });
 }

令我惊讶的是,仅仅这样做就可以使封装的 Flutter WebView 响应 touch# 事件,而不会遇到任何令人讨厌的 LongPress 问题。我不清楚为什么会发生这种情况 - 也许这里的其他人可以解释。

关于我上面的代码的几点说明

  • 我使用以横向格式占据整个设备屏幕的 WebView - iOS 和 Android
  • 我使用 WidgetsApp 而不是 MaterialApp 来提供基本的脚手架
  • SafeArea 确保该应用使用合法可用的 space
  • Listener 是这里的魔法所在,它使 Touch 事件在 WebView 中可用,即使在 Android
  • 上也是如此
  • WebView 是所有操作发生的主要参与者。在前端 JavaScript 你只需要在 document.body.
  • 上捕获 touchstarttouchmovetouchend 事件

您可以在 Flutter Docs.

中阅读更多关于我在这里使用的各种小部件的信息

以下是我的发现:据我所知,官方 Flutter webview 是一个被忽视的继子。有些错误可以追溯到三年前,但很少受到关注。它的一些 "features" 例如 longPress suppression/detection 是彻头彻尾的荒谬。

我改用 Flutter Webview plugin 解决了我遇到的大部分问题。请记住启用 withzoom:true。我发现 webview 不会自动捕获 onPauseonResume 事件。我是这样处理的

 @override void didChangeAppLifecycleState(AppLifecycleState state) 
 {
  setState(() 
  {
   appState = state;
   switch(state)
   {
    case AppLifecycleState.inactive:
    case AppLifecycleState.paused:
         FlutterWebviewPlugin.evalJavascript('callBack()');break;//onPause
    case AppLifecycleState.resumed:
         FlutterWebviewPlugin.evalJavascript('callBack()');break;//onResume 
   }
  });
 }

您必须确保

  • 您有一个适当初始化的 FlutterWebviewPlugin 单例
  • 实例
  • 在您的 Javascript 中您定义了 callBack

我应该提一下,我是从 WidgetsApp 开始的,上面的代码在有状态 class 中,它与 with WidgetsBindingObserver

一起使用