Flutter WebView双向通讯

时间:2018-12-09 05:28:15

标签: 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;

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

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

任何帮助将不胜感激。

5 个答案:

答案 0 :(得分:2)

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

在Flutter中,像这样构建WebView:

CFG_DATA_FILES := $(wildcard ../src/config/cfg_file*.csv)
CFG_DATA_MEM_FILES := $(addprefix bin/,$(notdir $(CFG_DATA_FILES:.csv=.mem)))
CFG_DATA_BIN_FILES := $(addprefix bin/,$(notdir $(CFG_DATA_MEM_FILES:.mem=.bin)))

COMBINED_BIN_FILES := $(subst cfg_file-,fw_,$(CFG_DATA_BIN_FILES))
COMBINED_BIN_FILES := $(subst .bin,_combined.bin,$(COMBINED_BIN_FILES))


ifdef CUSTOM_CONFIG
rom: build_images create_custom_binaries
else
rom: build_images create_binaries
endif

chk_custom_cfg:
ifdef CUSTOM_CONFIG
    export CUSTOM_CONFIG 
    $(eval CUST_CFG_DATA_FILES=$(CUSTOM_CONFIG)) 
    export CUST_CFG_DATA_FILES
    $(eval CUST_CFG_DATA_MEM_FILES=$(addprefix bin/,$(notdir $(CUST_CFG_DATA_FILES:.csv=.mem)))) 
    export CUST_CFG_DATA_MEM_FILES 
    $(eval CUST_CFG_DATA_BIN_FILES=$(addprefix bin/,$(notdir $(CUST_CFG_DATA_MEM_FILES:.mem=.bin))))
    export CUST_CFG_DATA_BIN_FILES 
    $(eval CUST_COMBINED_BIN_FILES=$(subst .bin,_combined.bin,$(CUST_CFG_DATA_BIN_FILES)))
    export CUST_COMBINED_BIN_FILES 
    @echo existing cfg files $(CUST_CFG_DATA_FILES)
    @echo existing mem files $(CUST_CFG_DATA_MEM_FILES)
    @echo existing bin files $(CUST_CFG_DATA_BIN_FILES)
    @echo existing combined files $(CUST_COMBINED_BIN_FILES)
endif
build_images: copy_ops_imgs 

create_binaries: config  $(COMBINED_BIN_FILES)

create_custom_binaries: chk_custom_cfg cust_config
    @echo custom config bin files $(CUST_CFG_DATA_BIN_FILES)

config: boot_partn_map_cfg $(CFG_DATA_BIN_FILES)
    @echo Removing temporary files
    @(cd bin; rm -rf *.mem)
cust_config: boot_partn_map_cfg $(CUST_CFG_DATA_BIN_FILES)
    @echo Removing temporary files
    @(cd bin; rm -rf *.mem) 

$(CFG_DATA_BIN_FILES): $(CFG_DATA_FILES)
    @echo Generating config data image $(subst /bin,,$@)

$(CUST_CFG_DATA_BIN_FILES):$(CUST_CFG_DATA_FILES)
    @echo Generating config data image $(subst /bin,,$@)

并在您的HTML文件中:

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

运行此代码时,您将能够在android studio的LogCat / Run窗口中看到日志“ 正在从Javascript代码调用Hello World ”。

答案 1 :(得分:2)

我想告诉您有关如何将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应用,则需要:

  1. 在您的js代码中
window.CHANNEL_NAME.postMessage('Hello from JS');
  1. 在您的浮动代码中。
    当您运行JavascriptChannel(name:'CHANNEL_NAME',...)
    flutter绑定到窗口,并使用您在构造函数中编写的名称(在这种情况下为CHANNEL_NAME)将WebView新的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代码的新手
因此,如果您对此还有其他更好的经验,可以在评论中写以帮助其他人!

答案 2 :(得分:2)

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

答案 3 :(得分:0)

您可以尝试使用我的插件flutter_inappbrowser编辑:它已重命名为flutter_inappwebview)并使用loading方法(请参阅更多here )。

下面是一个示例。 在Flutter方面:

loading:true

在JavaScript方面(例如,资产文件夹中的本地文件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}");
  },
),

...

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

assets/index.html

答案 4 :(得分:0)

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

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

1.从flutter到js监听器

created_at

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

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

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

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

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

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

附注。快乐编码