【问题标题】:Flutter Webview two way communication with JavascriptFlutter Webview 与 Javascript 两种方式通信
【发布时间】:2019-05-10 09:48:04
【问题描述】:

我有一个 html 文件,我正在使用 flutter_webview_plugin 在 Flutter webview 中加载该文件。我正在使用 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;

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

有什么方法可以做到吗?我已经评估了 interactive_webview 插件。它在 Android 上运行良好。但是,它有 swift 版本控制问题,不想再继续。

任何帮助将不胜感激。

【问题讨论】:

  • 嗨碎片。欢迎来到 StackOverflow!您能否编辑您的问题并改写它,以便它显示您已经尝试过并且目前陷入困境的内容?如果您包含任何错误消息/日志,其他人会更容易做出贡献。等
  • 谢谢艾哈迈德。我已经编辑了我的问题。我希望它有助于确定我面临的问题。
  • @shrad:我鼓励您选择一个答案作为接受的答案。
  • 有没有人找到解决方案将消息从 javascript 获取到 Flutter。

标签: javascript webview dart flutter


【解决方案1】:

这是一个从 Javascript 代码到 Flutter 的通信示例。

在 Flutter 中构建您的 WebView,如下所示:

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;
              },
            )

在您的 HTML 文件中:

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

当您运行此代码时,您应该能够在 android studio 的 LogCat/Run 窗口中看到日志“Hello World was called from Javascript code”。

【讨论】:

  • 我只是测试了你的脚本,但它不起作用:Print is not defined.
  • 这很奇怪。 Print 是您的 JavaScriptChannel 的名称。如果您使用不同的名称,则应在 JavaScript 中使用该名称。我希望你在 WebView 中调用网页。 (因为这不适用于移动/桌面浏览器)
  • 我做的另一种选择是创建一个函数并将Print.postMessage 放入函数中。然后用预期的事件调用它。但是您的脚本也可能有效,只是我不喜欢打开 html 时出现错误的想法。
  • Print 是频道名称,但我想从window.postMessage 获取消息,如何获取?
  • @sooon 预期的事件是什么?你是怎么做到的?
【解决方案2】:

你可以试试我的插件flutter_inappbrowser编辑:它已重命名为flutter_inappwebview)并使用addJavaScriptHandler({@required String handlerName, @required JavaScriptHandlerCallback callback})方法(查看更多here)。

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

...

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}");
  },
),

...

在 JavaScript 端(例如 assets 文件夹内的本地文件 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>

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

I/flutter (20436): From JavaScript side:
I/flutter (20436): [12, 2, 50]
I/flutter (20436): console message: 64

【讨论】:

    【解决方案3】:

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

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

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

    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,
            ),
          ),
        );
      }
    }
    

    但是如果我们想将这个 _webViewController 实例共享给抽屉等其他小部件怎么办?
    在这种情况下,我决定实现 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();
    }
    

    然后

    onWebViewCreated: (WebViewController controller) {
      var singleton = new Singleton(controller);
    },
    

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

    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),
                        ),
                      ],
                    ),
                  )),
            ],
          ),
        );
      }
    }
    

    如果你想从 JS 代码接收消息到你的 Flutter App 你需要:

    1. 在您的 js 代码中
    window.CHANNEL_NAME.postMessage('Hello from JS');
    
    1. 在您的颤振代码中。
      当您运行 JavascriptChannel(name: 'CHANNEL_NAME', ...)
      使用您在构造函数中编写的名称(在本例中为 CHANNEL_NAME
      因此,当我们致电 window.CHANNEL_NAME.postMessage('Hello from JS'); 时,我们会收到一条我们发送的消息
    WebView(
       javascriptChannels: [
         JavascriptChannel(name: 'CHANNEL_NAME', onMessageReceived: (message) {
           print(message.message);
           })
       ].toSet(),
      initialUrl: 'https://url.com',
    )
    

    所以我们到了。
    我是 Flutter 代码的新手
    因此,如果您对此有其他更好的体验,可以在 cmets 中编写以帮助其他人!

    【讨论】:

      【解决方案4】:

      使用flutter_inappwebview包的Javascript回调完整代码示例:

      import 'dart:async';
      import 'package:flutter/material.dart';
      import 'package:flutter_inappwebview/flutter_inappwebview.dart';
      
      Future main() async {
        WidgetsFlutterBinding.ensureInitialized();
        runApp(new MyApp());
      }
      
      class MyApp extends StatefulWidget {
        @override
        _MyAppState createState() => new _MyAppState();
      }
      
      class _MyAppState extends State<MyApp> {
        InAppWebViewController _webViewController;
      
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            home: Scaffold(
              appBar: AppBar(
                title: const Text('InAppWebView Example'),
              ),
              body: Container(
                  child: Column(children: <Widget>[
                Expanded(
                  child: InAppWebView(
                    initialData: InAppWebViewInitialData(data: """
      <!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">
          </head>
          <body>
              <h1>JavaScript Handlers (Channels) TEST</h1>
              <button id='test' onclick="window.flutter_inappwebview.callHandler('testFunc');">Test</button>
              <button id='testargs' onclick="window.flutter_inappwebview.callHandler('testFuncArgs', 1);">Test with Args</button>
              <button id='testreturn' onclick="window.flutter_inappwebview.callHandler('testFuncReturn').then(function(result) { alert(result);});">Test Return</button>
          </body>
      </html>
                        """),
                    initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                      debuggingEnabled: true,
                    )),
                    onWebViewCreated: (InAppWebViewController controller) {
                      _webViewController = controller;
      
                      _webViewController.addJavaScriptHandler(
                          handlerName: 'testFunc',
                          callback: (args) {
                            print(args);
                          });
      
                      _webViewController.addJavaScriptHandler(
                          handlerName: 'testFuncArgs',
                          callback: (args) {
                            print(args);
                          });
      
                      _webViewController.addJavaScriptHandler(
                          handlerName: 'testFuncReturn',
                          callback: (args) {
                            print(args);
                            return '2';
                          });
                    },
                    onConsoleMessage: (controller, consoleMessage) {
                      print(consoleMessage);
                    },
                  ),
                ),
              ])),
            ),
          );
        }
      }
      

      【讨论】:

        【解决方案5】:

        如果您使用支持 web、ios 和 android 的 webviewx 插件,那么我们可以进行双向通信。

        我有一个包含 index.html 和其他 js 的网页,以及我想在 webview 中显示并在 Flutter 和 web 应用程序之间进行通信的 css 页面。

        1.从flutter到js监听器

         IconButton(
                   icon: Icon(Icons.developer_mode),
                   onPressed: () {
                     webviewController
                         .evalRawJavascript('window.myFunction()',
                             inGlobalContext: false)
                         .then((value) => print(value));
                   },
                 )      
        

        注意:myFunction 是在 javascript 或 html 页面中定义的函数,如下所示。

        function myFunction() {
         alert("I am an alert box!");
         return 'working';
        }
        

        2。从 js/html 到 Flutter 监听器
        在 html/js 中添加带有监听器的按钮

        function submitClick() {
         var data = document.getElementById('data').value;
         SubmitCallback(data) //defined in flutter
        }
        

        现在在颤振中(添加 dartCallback):

         WebViewX(
                 javascriptMode: JavascriptMode.unrestricted,
                 initialContent: '<h2> Loading </h2>',
                 initialSourceType: SourceType.HTML,
                 onWebViewCreated: (controller) {
                   webviewController = controller;
                   _loadHtmlFromAssets();
                   webviewController.addListener(() {});
                 },
                 dartCallBacks: {
                   DartCallback(
                     name: 'SubmitCallback',
                     callBack: (msg) {
                       ScaffoldMessenger.of(context).showSnackBar(
                           SnackBar(content: Text('Submitted $msg successfully')));
                     },
                   ),
                 },
               )
        

        PS。快乐编码

        【讨论】:

        • 谢谢,对我有帮助
        【解决方案6】:

        有两种方式来传达答案:

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

        flutter 方面(使用按钮或触发方法):

        webViewController.evaluateJavascript('fromFlutter("pop")');
        

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

        javascript 方面 在 html 中,在您的正文标签中:

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

        第二种方式从你的 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
                },
            )
        ]),
        

        从 webview 中使用相同的通信名称“communicationname”(您可以在两个地方使用另一个名称):

          window.communicationname.postMessage("native,,,pop,");
        

        【讨论】:

          猜你喜欢
          • 2016-09-27
          • 1970-01-01
          • 1970-01-01
          • 2013-02-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-14
          • 1970-01-01
          相关资源
          最近更新 更多