目前flutter的webview相关插件,主要有三个:
webview_flutter(官方)flutter_webview_pluginflutter_inappwebview
经过调研测试后,可以简单对比一下:

webview_flutter(官方支持) flutter_webview_plugin flutter_inappwebview
接入复杂度 低,android启动有bug
支持alter、confim等 暂不支持 支持 支持
js与flutter互调 没问题 没问题 android10以下版本有bug 没问题
更新频率 非常新 超过2个月未更新 超过3个月未更新 非常新
支持加载asset的html 不支持(有解决方案) 不支持 支持 (Android5.0可能出现bug)

综合对比后,线上选择使用webview_flutter,本地资源 优先使用flutter_inappwebview

一、配置项目

pubspec.yaml配置

在 flutter 项目中的 pubspec.yaml 文件中添加:

👑webview_flutter:

  1. dependencies:
  2. webview_flutter: ^0.3.19+9

为了解决webview_flutter无法在iOS端直接加载asset中的html问题,暂时使用以下版本:

  1. dependencies:
  2. webview_flutter:
  3. git:
  4. url: https://github.com/asjqkkkk/plugins.git
  5. path: packages/webview_flutter
  6. ref: webview

👒flutter_inappwebview:

  1. flutter_inappwebview: ^5.3.2

二、加载本地资源

目录如下:
image.png
pubspec.yaml 中还需要添加相关文件声明

  1. assets:
  2. - assets/dist/
  3. - assets/dist/css/
  4. - assets/dist/img/
  5. - assets/dist/js/

👑webview_flutter:

Assetfiles

  1. WebView(
  2. onWebViewCreated: (WebViewController controller) {
  3. final url = 'assets/index.html';
  4. controller.loadAssetHtmlFile(url);
  5. },
  6. javascriptMode: JavascriptMode.unrestricted,
  7. );

👒flutter_inappwebview:

  1. InAppWebView(
  2. // initialUrl: 'www.baidu.com', //线上
  3. initialFile: "assets/dist/index.html", //本地
  4. ......
  5. )

三、与JavaScript相互调用

1、web调用原生、传参、回调

html代码:

  1. <script>
  2. //webview_flutter:
  3. function flutterCall(){
  4. var result = flutter.postMessage('js来啦');
  5. alert(result);
  6. }
  7. //flutter_inappwebview
  8. function flutterCall() {
  9. window.flutter_inappwebview.callHandler("flutter", "你要传给我的内容", ).then(function(args) {
  10. //收到回调参数后,你将它显示出来
  11. });
  12. }
  13. <script>

flutter代码:

  1. //webview_flutter:
  2. WebView(
  3. ...
  4. javascriptChannels: {
  5. JavascriptChannel(
  6. name: 'flutter',
  7. onMessageReceived: (JavascriptMessage message) {
  8. print('收到来自js:${message.message}');
  9. return '来自android';
  10. },
  11. ),
  12. },
  13. ...
  14. );
  15. //注意:这里回调是无效的,也就是result为null
  16. //👒flutter_inappwebview:
  17. onLoadStop:(InAppWebViewController controller, String url) async {
  18. _webcontrl.addJavaScriptHandler(
  19. handlerName: "flutter",
  20. callback: (arg) {
  21. //接收web的调用和传参arg
  22. return '回调web';
  23. });
  24. },

2、原生调用web、传参、回调

html代码:

  1. <script>
  2. //被flutter调用的方法
  3. function onFlutterCall(result){
  4. console.log(result, typeof result);
  5. alert(result);
  6. return result;
  7. }
  8. <script>

flutter代码:

  1. //webview_flutter
  2. WebView(
  3. ...
  4. onWebViewCreated: (WebViewController controller) {
  5. _controller = controller;
  6. debugPrint('onWebViewCreated成功');
  7. },
  8. ...
  9. );
  10. _controller.evaluateJavascript('onFlutterCall("aaa")');
  11. //flutter_inappwebview
  12. _controller.evaluateJavascript(source: "onFlutterCall('aaa')").then((value) => print(value));

3.通过alert 、confirm、prompt通信

注意:在webview_flutter中,alert 、confirm、prompt等方法是无效的
但,flutter_inappwebview 均没问题

  1. InAppWebView(
  2. onLoadStop: (controller, uri) {
  3. _controller = controller;
  4. _controller.addJavaScriptHandler(
  5. handlerName: "Flutter",
  6. callback: (list) {
  7. print("js传过来的--->$list");
  8. return "flutter表示收到了";
  9. });
  10. },
  11. onJsPrompt:
  12. (InAppWebViewController controller, JsPromptRequest request) async {
  13. print("prompt:---->${request.message}");
  14. JsPromptResponse response =
  15. JsPromptResponse(message: "flutter-prompt: hello", value: "ok");
  16. return response;
  17. },
  18. onJsConfirm: (InAppWebViewController controller,
  19. JsConfirmRequest request) async {
  20. print("Confirm:---->${request.message}");
  21. JsConfirmResponse response = JsConfirmResponse(
  22. message: "flutter-confirm: hello", handledByClient: true);
  23. return response;
  24. },
  25. onJsAlert:
  26. (InAppWebViewController controller, JsAlertRequest request) async {
  27. print("Alert:---->${request.message}");
  28. JsAlertResponse response =
  29. JsAlertResponse(message: "flutter-alert: hello");
  30. return response;
  31. },
  32. )
  33. // html:
  34. var result = window.confirm("js:hello");
  35. var result = window.prompt("js:hello");
  36. var result = alert("js:hello");

效果参考:
demo.gif

4.嵌入iframe显示与交互

一、加载显示

  1. import 'dart:html';
  2. import 'dart:js_util';
  3. import 'dart:ui' as ui;
  4. import 'dart:js' ;
  5. import 'dart:html' as html;
  6. import 'package:flutter/material.dart';
  7. //声明
  8. html.IFrameElement _element;
  9. // js.JsObject _connector;
  10. String viewId = UniqueKey().toString();
  11. @override
  12. void initState() {
  13. super.initState();
  14. setProperty(window, "callFlutter", allowInterop(callFlutter));
  15. // context.callMethod("init");
  16. html.window.onMessage.listen((MessageEvent event) {
  17. print("收到---${event.data}");
  18. });
  19. // js.context["connect_content_to_flutter"] = (content) {
  20. // _connector = content;
  21. // };
  22. // js.context.callMethod('jsFunc', ["--args--"]);
  23. _element = html.IFrameElement()
  24. // ..src = "http://172.22.6.16:9997/#/"
  25. ..style.border = 'none'
  26. ..src = "assets/web/dist/wk.html";
  27. /* ..srcdoc = """
  28. <!DOCTYPE html>
  29. <head>
  30. <script>
  31. // variant 1----
  32. parent.connect_content_to_flutter && parent.connect_content_to_flutter(window)
  33. function hello(msg) {
  34. alert(msg)
  35. }
  36. // variant 2-----
  37. window.addEventListener("message", (message) => {
  38. if (message.data.id === "test") {
  39. alert(message.data.msg)
  40. }
  41. })
  42. function postMessageToFlutter() {
  43. window.parent.postMessage("js--888");
  44. }
  45. function jsToFlutter() {
  46. window.parent.callFlutter("js--999");
  47. }
  48. </script>
  49. </head>
  50. <body>
  51. <h2>I'm IFrame</h2>
  52. <button onclick="postMessageToFlutter()">postmessage</button>
  53. <button onclick="jsToFlutter()">js->flutter</button>
  54. </body>
  55. </html>
  56. """; */
  57. // ignore:undefined_prefixed_name
  58. ui.platformViewRegistry.registerViewFactory(
  59. viewId,
  60. (int viewId) => _element,
  61. );
  62. }

二、交互通信

  • 通过postMessage
  • 通过setProperty监听 ,callMethod 发送 ```dart //postMessage //dart 监听 html.window.onMessage.listen((MessageEvent event) { print(“收到—-${event.data}”); });

//js发送 window.parent.postMessage(“js—888”);


// dart发送

第一种:html.window.postMessage(//data to send here, “*”);

第二种:_element.contentWindow.postMessage({ ‘id’: ‘test’, ‘msg’: ‘Hello from second variant’, }, “*”);

// js监听

window.addEventListener(“message”, (message) => { if (message.data.id === “test”) { alert(message.data.msg) } })

  1. ```dart
  2. //通过setProperty监听 ,callMethod 发送
  3. --------------dart -> js--------------------
  4. //dart
  5. js.JsObject _connector; // 声明
  6. //保存链接window
  7. js.context["connect_content_to_flutter"] = (content) {
  8. _connector = content;
  9. };
  10. //发送
  11. _connector.callMethod('hello', ['Hello from first variant']);
  12. // js
  13. //注册关联
  14. (parent.connect_content_to_flutter && parent.connect_content_to_flutter(window))
  15. //接收
  16. function hello(msg) {
  17. alert(msg)
  18. }
  19. 另一种方法:(不用注册关联,相对简单)
  20. //js
  21. window.parent.hello = function hello(str) {
  22. alert(`js收到:${str}`);
  23. }
  24. //dart 调用
  25. js.context.callMethod("hello",["123"]);
  26. --------------js -> dart--------------------
  27. //js调用
  28. window.parent.callFlutter("js--999");
  29. //dart
  30. //提前注册关联
  31. setProperty(window, "callFlutter", allowInterop(callFlutter));
  32. //接收
  33. void callFlutter(String str) {
  34. print("收到:--->$str");
  35. }

四、线上加载web

  1. return WebView(
  2. initialUrl: '在这里传入需要加载的链接',
  3. javascriptMode: JavascriptMode.unrestricted,
  4. );

五、离线加载web

  1. WebView(
  2. onWebViewCreated: (WebViewController controller) {
  3. controller.loadLocalHtmlFile(filePath)
  4. },
  5. javascriptMode: JavascriptMode.unrestricted,
  6. );
  7. Future<void> downloadFile(String url) async {
  8. Dio dio = Dio();
  9. String path = (await getApplicationDocumentsDirectory()).path;
  10. String fileName=url.substring(url.lastIndexOf("/")+1);
  11. await Dio().download(url, '$path/$fileName',onReceiveProgress: (rec,total){
  12. progress = ((rec/total) * 100).floor();
  13. if(progress >= 100){
  14. filePath= '$path/$fileName';
  15. }
  16. setState(() {});
  17. });
  18. }

demo.gif

下面的[1、2、3]操作具体可以参考:
https://gitlab.com/NewFish/flutter_webview_test

1、工具使用

path_provider 统一路径
archive 文件解压
webview_flutter web资源解析
dio 网络框架

2、自定义插件 - webview_flutter(修复ios解析本地资源)

  1. webview_flutter:
  2. git:
  3. url: https://gitee.com/oldBen/plugins.git
  4. path: packages/webview_flutter
  5. ref: webview

3、扩展

https://inappwebview.dev/docs/javascript/communication/
https://cloud.tencent.com/developer/news/667143

Inappwebview Demo:
flu_web.zip