繁体   English   中英

Flutter Webview 与 Javascript 双向通信

[英]Flutter Webview two way communication with Javascript

我有一个 html 文件,我正在使用 flutter_webview_plugin 在 Flutter webview 中加载该文件。 我正在使用 evalJavascript 在我的 javascript 代码中调用 function,意思是颤振(飞镖)-> 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 版本控制问题,不想进一步处理。

任何帮助,将不胜感激。

这是一个从 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 is called from Javascript code ”。

您可以尝试我的插件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}");
  },
),

...

在 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>

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

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

我想告诉你如何从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实例共享给其他像 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();
}

然后

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 应用程序,你需要:

  1. 在你的 js 代码中
window.CHANNEL_NAME.postMessage('Hello from JS');
  1. 在你的颤振代码中。
    当你运行 JavascriptChannel(name: 'CHANNEL_NAME', ...)
    使用您在构造函数中编写的名称(在本例中为CHANNEL_NAME )将 flutter 绑定到您的窗口 WebView new MessageChannel
    所以当我们调用window.CHANNEL_NAME.postMessage('Hello from JS'); 我们收到一条我们发送的消息
WebView(
   javascriptChannels: [
     JavascriptChannel(name: 'CHANNEL_NAME', onMessageReceived: (message) {
       print(message.message);
       })
   ].toSet(),
  initialUrl: 'https://url.com',
)

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

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

如果您使用支持 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')));
             },
           ),
         },
       )

附注。 快乐编码

两种方式交流回答:

Flutter 到 webview(javascript,react...)

从颤振方面(使用按钮或颤振的触发方法):

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

这个“fromFlutter”将是你的javascript中方法的名称,react,不管你也可以发送文本,在这种情况下是“pop”。

从 html 内部的 javascript 端,在您的正文标签中:

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

从使用相同通信名称“comunicationname”的 web 视图(您可以在两个地方使用另一个名称):

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

我希望这对你有用。 快乐编码。

Flutter 3.0.5 webview_flutter:^3.0.4 flutter_js:^0.5.0+6

使用 JavascriptChannels 的另一种方法是将数据从“应用程序”传输到您的网站。

Dart:

JavascriptChannel(

          name: 'getFCMToken',
          onMessageReceived: (JavascriptMessage message) async {
            //print(message.message);

            final token = (await FirebaseMessaging.instance.getToken())!;
            final script = "var appToken =\"${token }\"";
            _webViewController.runJavascript(script);

          },
        ),

html:

<script  type = "text/javascript">
    window.onload = getFCMToken.postMessage('');
</script>

或飞镖(触发器):

OnPageFinished: (url) async {
   try {
 final token = (await FirebaseMessaging.instance.getToken())!;     
 var javascript = "var appToken=\"${token.toString()}\"";
       } catch (_) {}
}

因此,在您的网站代码中,您有一个 js var“appToken”,您可以在 PHP 或其他任何地方使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM