简体   繁体   English

Flutter Webview 与 Javascript 双向通信

[英]Flutter Webview two way communication with Javascript

I have an html file that I am loading in Flutter webview using flutter_webview_plugin.我有一个 html 文件,我正在使用 flutter_webview_plugin 在 Flutter webview 中加载该文件。 I am using evalJavascript to call function in my javascript code, meaning flutter(dart)->js.我正在使用 evalJavascript 在我的 javascript 代码中调用 function,意思是颤振(飞镖)-> js。 However, I also need some way to communicate back something to flutter(dart) layer, meaning js->flutter(dart).但是,我还需要某种方式将某些内容传回 Flutter(dart) 层,即 js->flutter(dart)。

I have tried using - webkit.messageHandlers.native - window.native to support both platforms(Android,iOS) checking if those are available in JS.我尝试使用 - webkit.messageHandlers.native - window.native 来支持两个平台(Android,iOS),检查它们是否在 JS 中可用。 But, those comes as undefined.但是,这些都是未定义的。 Using following code to get instance of native handler in JS.使用以下代码在 JS 中获取本机处理程序的实例。

typeof webkit !== 'undefined' ? webkit.messageHandlers.native : 
window.native;

And even if I get that instance and post message using it, not sure how to handle it in flutter(dart) layer.即使我得到该实例并使用它发布消息,也不知道如何在颤振(飞镖)层中处理它。 I may need to use platform channels.我可能需要使用平台渠道。 Not sure, if I am in the right direction.不确定,如果我在正确的方向。

Is there any way through which I can do that?有什么办法可以做到吗? I have evaluated interactive_webview plugin.我已经评估了 interactive_webview 插件。 It works fine on Android.它在 Android 上运行良好。 But, it has swift versioning issue and don't want to proceed further with that.但是,它有 swift 版本控制问题,不想进一步处理。

Any help would be appreciated.任何帮助,将不胜感激。

Here is an example of communication from Javascript code to flutter.这是一个从 Javascript 代码到 flutter 的通信示例。

In Flutter build your WebView like :在 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;
              },
            )

and in Your HTMLfile:并在您的 HTML 文件中:

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

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.当您运行此代码时,您将能够在 android studio 的 LogCat/Run 窗口中看到日志“ Hello World is called from Javascript code ”。

You can try my plugin flutter_inappbrowser ( EDIT : it has been renamed to flutter_inappwebview ) and use addJavaScriptHandler({@required String handlerName, @required JavaScriptHandlerCallback callback}) method (see more here ).您可以尝试我的插件flutter_inappbrowser编辑:它已重命名为flutter_inappwebview )并使用addJavaScriptHandler({@required String handlerName, @required JavaScriptHandlerCallback callback})方法( 在此处查看更多信息)。

An example is presented below.下面给出一个例子。 On Flutter side:在颤振方面:

...

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

...

On JavaScript side (for example a local file assets/index.html inside the assets folder):在 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>

On Android Studio logs you will get:在 Android Studio 日志中,您将获得:

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

I want to tell you about how to send messages from flutter WebView to JS:我想告诉你如何从flutter WebView向JS发送消息:

  1. In JS code you need to bind your function you need to fire to window在 JS 代码中,您需要将需要触发的函数绑定到窗口
const function = () => alert('hello from JS');
window.function = function;
  1. In your code in WebView widget implementation you need to declare onWebViewCreated method like this在 WebView 小部件实现中的代码中,您需要像这样声明onWebViewCreated方法
WebView(
  onWebViewCreated: (WebViewController controller) {},
  initialUrl: 'https://url.com',
  javascriptMode: JavascriptMode.unrestricted,
)
  1. In class widget declare var _webViewController;在类小部件中声明var _webViewController;
class App extends State<MyApp> {
  final _webViewController;
}
  1. In onWebViewCreated write this codeonWebViewCreated 中编写此代码
onWebViewCreated: (WebViewController controller) {
    _webViewController = controller;
},

Then you can run code like this:然后你可以运行这样的代码:

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

But what if we want to share this _webViewController instance to other widgets like drawer?但是如果我们想将这个_webViewController实例共享给其他像 drawer 这样的小部件怎么办?
In this case I decided to implement Singleton pattern and store _webViewController instance in it.在这种情况下,我决定实现Singleton pattern并在其中存储_webViewController实例。
So所以
Singleton class单例类

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

Then然后

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

And finally in our Drawer widget ie (here you can use whatever widget you want)最后在我们的抽屉小部件即(在这里你可以使用任何你想要的小部件)

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

If you want to receive messages from JS code to your flutter App you need:如果你想从 JS 代码接收消息到你的 Flutter 应用程序,你需要:

  1. In your js code在你的 js 代码中
window.CHANNEL_NAME.postMessage('Hello from JS');
  1. In your flutter code.在你的颤振代码中。
    When you're running JavascriptChannel(name: 'CHANNEL_NAME', ...)当你运行 JavascriptChannel(name: 'CHANNEL_NAME', ...)
    flutter bind to your window WebView new MessageChannel with name you wrote in constructor (in this case CHANNEL_NAME )使用您在构造函数中编写的名称(在本例中为CHANNEL_NAME )将 flutter 绑定到您的窗口 WebView new MessageChannel
    so when we call window.CHANNEL_NAME.postMessage('Hello from JS');所以当我们调用window.CHANNEL_NAME.postMessage('Hello from JS'); we recieve a message we sent我们收到一条我们发送的消息
WebView(
   javascriptChannels: [
     JavascriptChannel(name: 'CHANNEL_NAME', onMessageReceived: (message) {
       print(message.message);
       })
   ].toSet(),
  initialUrl: 'https://url.com',
)

So here we are.所以我们来了。
I'm new in flutter code我是颤振代码的新手
So if you have another better experience about this you can write in comments to help other people!因此,如果您对此有其他更好的体验,可以在评论中写下以帮助其他人!

Full code example of Javascript callbacks using package flutter_inappwebview:使用包 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);
              },
            ),
          ),
        ])),
      ),
    );
  }
}

If you are using webviewx plugin which support web,ios and android than this is how we can do two way communication.如果您使用支持 web、ios 和 android 的webviewx插件,那么我们可以进行两种方式的通信。

I have webpage which has index.html and other js,and css pages which I want to display in webview and communicate between flutter and web app.我有一个包含 index.html 和其他 js 的网页,以及我想在 webview 中显示并在 flutter 和 web 应用程序之间进行通信的 css 页面。

1. From flutter to js listener 1.从flutter到js监听器

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

Note: myFunction is function defined in javascript or html page as below.注意:myFunction 是在 javascript 或 html 页面中定义的函数,如下所示。

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

2. From js/html to flutter listener 2.从js/html到flutter监听器
In html/js add button with listener在 html/js 中添加带有监听器的按钮

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

Now In flutter(add dartCallback):现在在颤振中(添加 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.附注。 Happy Coding快乐编码

Two ways comunication answer:两种方式交流回答:

Flutter to webview (javascript, react...) Flutter 到 webview(javascript,react...)

From the flutter side (using a button or in a trigger method from flutter):从颤振方面(使用按钮或颤振的触发方法):

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

This "fromFlutter" will be the name of the method in your javascript, react, whatever and also you can send text, in this case is "pop".这个“fromFlutter”将是你的javascript中方法的名称,react,不管你也可以发送文本,在这种情况下是“pop”。

From the javascript side inside the html, in your body label:从 html 内部的 javascript 端,在您的正文标签中:

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

  </script>

From your webview (javascript, react...) to Flutter从你的 webview(javascript,react...)到 Flutter

In your Webview attribute javascriptChannels you can add:在您的 Webview 属性 javascriptChannels 中,您可以添加:

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

From the webview using the same comunication name "comunicationname" (your can use another name in both places):从使用相同通信名称“comunicationname”的 web 视图(您可以在两个地方使用另一个名称):

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

I hope this work for your.我希望这对你有用。 Happy coding.快乐编码。

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

Another way to use JavascriptChannels is to tranfer data from the "App" to your Website.使用 JavascriptChannels 的另一种方法是将数据从“应用程序”传输到您的网站。

Dart: 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: html:

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

or Dart(Trigger):或飞镖(触发器):

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

so in your website code you have a js var "appToken" wich you can use in PHP or whatever.因此,在您的网站代码中,您有一个 js var“appToken”,您可以在 PHP 或其他任何地方使用它。

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

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