Flutter WebView双向通讯

shr*_*rad 3 javascript webview dart flutter

我有一个使用flutter_webview_plugin在Flutter Webview中加载的html文件。我正在使用evalJavascript在我的JavaScript代码中调用函数,这意味着flutter(dart)-> js。但是,我还需要某种方法将某些内容传达回flutter(dart)层,这意味着js-> flutter(dart)。

我尝试使用-webkit.messageHandlers.native-window.native来支持两个平台(Android,iOS),检查它们是否在JS中可用。但是,这些是不确定的。使用以下代码获取JS中本机处理程序的实例。

typeof webkit !== 'undefined' ? webkit.messageHandlers.native : 
window.native;
Run Code Online (Sandbox Code Playgroud)

即使我得到该实例并使用它发布消息,也不确定如何在flutter(dart)层中处理它。我可能需要使用平台渠道。不确定,如果我的方向正确。

有什么办法可以做到这一点?我已经评估了Interactive_webview插件。它在Android上运行良好。但是,它具有快速的版本控制问题,因此不希望进一步进行。

任何帮助,将不胜感激。

Lor*_*lli 15

您可以尝试我的插件flutter_inappbrowser编辑:它已重命名为flutter_inappwebview)和使用addJavaScriptHandler({@required String handlerName, @required JavaScriptHandlerCallback callback})方法(在此处查看更多信息)。

下面给出一个例子。在颤振方面:

...

child: InAppWebView(
  initialFile: "assets/index.html",
  initialHeaders: {},
  initialOptions: InAppWebViewWidgetOptions(
    inAppWebViewOptions: InAppWebViewOptions(
        debuggingEnabled: true,
    )
  ),
  onWebViewCreated: (InAppWebViewController controller) {
    webView = controller;

    controller.addJavaScriptHandler(handlerName: "mySum", callback: (args) {
      // Here you receive all the arguments from the JavaScript side 
      // that is a List<dynamic>
      print("From the JavaScript side:");
      print(args);
      return args.reduce((curr, next) => curr + next);
    });
  },
  onLoadStart: (InAppWebViewController controller, String url) {

  },
  onLoadStop: (InAppWebViewController controller, String url) {

  },
  onConsoleMessage: (InAppWebViewController controller, ConsoleMessage consoleMessage) {
    print("console message: ${consoleMessage.message}");
  },
),

...
Run Code Online (Sandbox Code Playgroud)

在 JavaScript 端(例如assets/index.html资产文件夹中的本地文件):

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Flutter InAppBrowser</title>

        ...

    </head>
    <body>

        ...

        <script>
           // In order to call window.flutter_inappwebview.callHandler(handlerName <String>, ...args) 
           // properly, you need to wait and listen the JavaScript event flutterInAppWebViewPlatformReady. 
           // This event will be dispatched as soon as the platform (Android or iOS) is ready to handle the callHandler method. 
           window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
             // call flutter handler with name 'mySum' and pass one or more arguments
             window.flutter_inappwebview.callHandler('mySum', 12, 2, 50).then(function(result) {
               // get result from Flutter side. It will be the number 64.
               console.log(result);
             });
           });
        </script>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

在 Android Studio 日志中,您将获得:

I/flutter (20436): From JavaScript side:
I/flutter (20436): [12, 2, 50]
I/flutter (20436): console message: 64
Run Code Online (Sandbox Code Playgroud)


小智 10

我想告诉你如何从flutter WebView向JS发送消息:

  1. 在 JS 代码中,您需要将需要触发的函数绑定到窗口
const function = () => alert('hello from JS');
window.function = function;
Run Code Online (Sandbox Code Playgroud)
  1. 在 WebView 小部件实现中的代码中,您需要像这样声明onWebViewCreated方法
WebView(
  onWebViewCreated: (WebViewController controller) {},
  initialUrl: 'https://url.com',
  javascriptMode: JavascriptMode.unrestricted,
)
Run Code Online (Sandbox Code Playgroud)
  1. 在类小部件中声明 var _webViewController;
class App extends State<MyApp> {
  final _webViewController;
}
Run Code Online (Sandbox Code Playgroud)
  1. onWebViewCreated 中编写此代码
onWebViewCreated: (WebViewController controller) {
    _webViewController = controller;
},
Run Code Online (Sandbox Code Playgroud)

然后你可以运行这样的代码:

class App extends StatelessWidget {
  var _webViewController;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        body: WebView(
          onWebViewCreated: (WebViewController controller) {
            _webViewController = controller;
          },
          initialUrl: 'https://url.com',
          javascriptMode: JavascriptMode.unrestricted,
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // When you click at this button youll run js code and youll see alert
            _webViewController
                .evaluateJavascript('window.function ()');
          },
          child: Icon(Icons.add),
          backgroundColor: Colors.green,
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

但是如果我们想把这个_webViewController实例分享给其他像 drawer这样的小部件怎么办?
在这种情况下,我决定在其中实现Singleton pattern和存储_webViewController实例。
所以
单例类

class Singleton {
  WebViewController webViewController;

  static final Singleton _singleton = new Singleton._internal();

  static Singleton get instance => _singleton;

  factory Singleton(WebViewController webViewController) {
    _singleton.webViewController = webViewController;
    return _singleton;
  }

  Singleton._internal();
}
Run Code Online (Sandbox Code Playgroud)

然后

onWebViewCreated: (WebViewController controller) {
  var singleton = new Singleton(controller);
},
Run Code Online (Sandbox Code Playgroud)

最后在我们的抽屉小部件即(在这里你可以使用任何你想要的小部件)

class EndDrawer extends StatelessWidget {
  final singleton = Singleton.instance;

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          SizedBox(
              width: 200,
              child: FlatButton(
                onPressed: () {
                  singleton.webViewController.evaluateJavascript('window.function()');
                  Navigator.pop(context); // exit drawer
                },
                child: Row(
                  children: <Widget>[
                    Icon(
                      Icons.exit_to_app,
                      color: Colors.redAccent,
                    ),
                    SizedBox(
                      width: 30,
                    ),
                    Text(
                      'Exit',
                      style: TextStyle(color: Colors.blueAccent, fontSize: 20),
                    ),
                  ],
                ),
              )),
        ],
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

如果你想从 JS 代码接收消息到你的 Flutter 应用程序,你需要:

  1. 在你的 js 代码中
window.CHANNEL_NAME.postMessage('Hello from JS');
Run Code Online (Sandbox Code Playgroud)
  1. 在你的颤振代码中。
    当您运行 JavascriptChannel(name: 'CHANNEL_NAME', ...) 时,
    使用您在构造函数中编写的名称(在本例中CHANNEL_NAME)颤动绑定到您的窗口 WebView new MessageChannel
    所以当我们打电话时,window.CHANNEL_NAME.postMessage('Hello from JS');我们会收到我们发送的消息
window.CHANNEL_NAME.postMessage('Hello from JS');
Run Code Online (Sandbox Code Playgroud)

所以我们来了。
我是颤振代码的新手
所以如果你有其他更好的经验,你可以在评论中写来帮助其他人!

  • 我已经使用了第二部分,讨论从 JS cod 发送消息到 flutter,但是当我使用 `window.CHANNEL_NAME.postMessage(" 时,我收到此错误 `TypeError: Cannot readproperties of undefined (reading 'postMessage')`悲伤”)` (2认同)

Sur*_*mar 7

Here is an example of communication from Javascript code to flutter.

In Flutter build your WebView like :

WebView(
              initialUrl: url,
              javascriptMode: JavascriptMode.unrestricted,
              javascriptChannels: Set.from([
                JavascriptChannel(
                    name: 'Print',
                    onMessageReceived: (JavascriptMessage message) {
                      //This is where you receive message from 
                      //javascript code and handle in Flutter/Dart
                      //like here, the message is just being printed
                      //in Run/LogCat window of android studio
                      print(message.message);
                    })
              ]),
              onWebViewCreated: (WebViewController w) {
                webViewController = w;
              },
            )
Run Code Online (Sandbox Code Playgroud)

and in Your HTMLfile:

<script type='text/javascript'>
    Print.postMessage('Hello World being called from Javascript code');
</script>
Run Code Online (Sandbox Code Playgroud)

When you run this code, you shall be able to see log "Hello World being called from Javascript code" in the LogCat/Run window of android studio.

  • 我只是测试了你的脚本,但它不起作用:“打印未定义”。 (4认同)
  • 这很奇怪。Print 是您的 JavaScriptChannel 的名称。如果您使用不同的名称,则应在 JavaScript 中使用该名称。我希望你在WebView中调用网页。(因为这不适用于移动/桌面浏览器) (4认同)

Ped*_*ina 5

有两种方式可以传达答案:

第一种方式从 Flutter 到 webview(javascript、react...)

颤振侧(使用按钮或触发方法):

webViewController.evaluateJavascript('fromFlutter("pop")');
Run Code Online (Sandbox Code Playgroud)

fromFlutter将是你的 javascript、react 等方法的名称,你也可以发送文本,在本例中为“pop”。

从html 内的javascript 端,在你的 body 标签中:

<script type="text/javascript">
    function fromFlutter(data) {
     // Do something
     console.log("This is working now!!!");
    }

  </script>
Run Code Online (Sandbox Code Playgroud)

第二种方式从你的webview(javascript,react...)到Flutter

在您的 Webview 属性中,javascriptChannels您可以添加:

javascriptChannels: Set.from([
     JavascriptChannel(
        name: 'comunicationname',
        onMessageReceived: (JavascriptMessage message) async {
          // Here you can take message.message and use 
          // your string from webview
        },
    )
]),
Run Code Online (Sandbox Code Playgroud)

从网络视图中使用相同的通信名称“communicationname”(您可以在两个地方使用其他名称):

  window.communicationname.postMessage("native,,,pop,");
Run Code Online (Sandbox Code Playgroud)