目前flutter的webview相关插件,主要有三个:
webview_flutter(官方)、 flutter_webview_plugin 和 flutter_inappwebview
经过调研测试后,可以简单对比一下:
| webview_flutter(官方支持) | flutter_webview_plugin | flutter_inappwebview | |
|---|---|---|---|
| 接入复杂度 | 低 | 低 | 低, |
| 支持alter、confim等 | 暂不支持 | 支持 | 支持 |
| js与flutter互调 | 没问题 | 没问题 | |
| 更新频率 | 非常新 | 超过2个月未更新 | |
| 支持加载asset的html | 不支持(有解决方案 |
不支持 | 支持 |
综合对比后,线上选择使用webview_flutter,本地资源 优先使用flutter_inappwebview
一、配置项目
pubspec.yaml配置
在 flutter 项目中的 pubspec.yaml 文件中添加:
👑webview_flutter:
dependencies:webview_flutter: ^0.3.19+9
为了解决webview_flutter无法在iOS端直接加载asset中的html问题,暂时使用以下版本:
dependencies:webview_flutter:git:url: https://github.com/asjqkkkk/plugins.gitpath: packages/webview_flutterref: webview
👒flutter_inappwebview:
flutter_inappwebview: ^5.3.2
二、加载本地资源
目录如下:
pubspec.yaml 中还需要添加相关文件声明
assets:- assets/dist/- assets/dist/css/- assets/dist/img/- assets/dist/js/
👑webview_flutter:
Assetfiles
WebView(onWebViewCreated: (WebViewController controller) {final url = 'assets/index.html';controller.loadAssetHtmlFile(url);},javascriptMode: JavascriptMode.unrestricted,);
👒flutter_inappwebview:
InAppWebView(// initialUrl: 'www.baidu.com', //线上initialFile: "assets/dist/index.html", //本地......)
三、与JavaScript相互调用
1、web调用原生、传参、回调
html代码:
<script>//webview_flutter:function flutterCall(){var result = flutter.postMessage('js来啦');alert(result);}//flutter_inappwebviewfunction flutterCall() {window.flutter_inappwebview.callHandler("flutter", "你要传给我的内容", ).then(function(args) {//收到回调参数后,你将它显示出来});}<script>
flutter代码:
//webview_flutter:WebView(...javascriptChannels: {JavascriptChannel(name: 'flutter',onMessageReceived: (JavascriptMessage message) {print('收到来自js:${message.message}');return '来自android';},),},...);//注意:这里回调是无效的,也就是result为null//👒flutter_inappwebview:onLoadStop:(InAppWebViewController controller, String url) async {_webcontrl.addJavaScriptHandler(handlerName: "flutter",callback: (arg) {//接收web的调用和传参argreturn '回调web';});},
2、原生调用web、传参、回调
html代码:
<script>//被flutter调用的方法function onFlutterCall(result){console.log(result, typeof result);alert(result);return result;}<script>
flutter代码:
//webview_flutterWebView(...onWebViewCreated: (WebViewController controller) {_controller = controller;debugPrint('onWebViewCreated成功');},...);_controller.evaluateJavascript('onFlutterCall("aaa")');//flutter_inappwebview_controller.evaluateJavascript(source: "onFlutterCall('aaa')").then((value) => print(value));
3.通过alert 、confirm、prompt通信
注意:在webview_flutter中,alert 、confirm、prompt等方法是无效的
但,flutter_inappwebview 均没问题
InAppWebView(onLoadStop: (controller, uri) {_controller = controller;_controller.addJavaScriptHandler(handlerName: "Flutter",callback: (list) {print("js传过来的--->$list");return "flutter表示收到了";});},onJsPrompt:(InAppWebViewController controller, JsPromptRequest request) async {print("prompt:---->${request.message}");JsPromptResponse response =JsPromptResponse(message: "flutter-prompt: hello", value: "ok");return response;},onJsConfirm: (InAppWebViewController controller,JsConfirmRequest request) async {print("Confirm:---->${request.message}");JsConfirmResponse response = JsConfirmResponse(message: "flutter-confirm: hello", handledByClient: true);return response;},onJsAlert:(InAppWebViewController controller, JsAlertRequest request) async {print("Alert:---->${request.message}");JsAlertResponse response =JsAlertResponse(message: "flutter-alert: hello");return response;},)// html:var result = window.confirm("js:hello");var result = window.prompt("js:hello");var result = alert("js:hello");
效果参考:
4.嵌入iframe显示与交互
一、加载显示
import 'dart:html';import 'dart:js_util';import 'dart:ui' as ui;import 'dart:js' ;import 'dart:html' as html;import 'package:flutter/material.dart';//声明html.IFrameElement _element;// js.JsObject _connector;String viewId = UniqueKey().toString();@overridevoid initState() {super.initState();setProperty(window, "callFlutter", allowInterop(callFlutter));// context.callMethod("init");html.window.onMessage.listen((MessageEvent event) {print("收到---${event.data}");});// js.context["connect_content_to_flutter"] = (content) {// _connector = content;// };// js.context.callMethod('jsFunc', ["--args--"]);_element = html.IFrameElement()// ..src = "http://172.22.6.16:9997/#/"..style.border = 'none'..src = "assets/web/dist/wk.html";/* ..srcdoc = """<!DOCTYPE html><head><script>// variant 1----parent.connect_content_to_flutter && parent.connect_content_to_flutter(window)function hello(msg) {alert(msg)}// variant 2-----window.addEventListener("message", (message) => {if (message.data.id === "test") {alert(message.data.msg)}})function postMessageToFlutter() {window.parent.postMessage("js--888");}function jsToFlutter() {window.parent.callFlutter("js--999");}</script></head><body><h2>I'm IFrame</h2><button onclick="postMessageToFlutter()">postmessage</button><button onclick="jsToFlutter()">js->flutter</button></body></html>"""; */// ignore:undefined_prefixed_nameui.platformViewRegistry.registerViewFactory(viewId,(int viewId) => _element,);}
二、交互通信
- 通过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) } })
```dart//通过setProperty监听 ,callMethod 发送--------------dart -> js--------------------//dartjs.JsObject _connector; // 声明//保存链接windowjs.context["connect_content_to_flutter"] = (content) {_connector = content;};//发送_connector.callMethod('hello', ['Hello from first variant']);// js//注册关联(parent.connect_content_to_flutter && parent.connect_content_to_flutter(window))//接收function hello(msg) {alert(msg)}另一种方法:(不用注册关联,相对简单)//jswindow.parent.hello = function hello(str) {alert(`js收到:${str}`);}//dart 调用js.context.callMethod("hello",["123"]);--------------js -> dart--------------------//js调用window.parent.callFlutter("js--999");//dart//提前注册关联setProperty(window, "callFlutter", allowInterop(callFlutter));//接收void callFlutter(String str) {print("收到:--->$str");}
四、线上加载web
return WebView(initialUrl: '在这里传入需要加载的链接',javascriptMode: JavascriptMode.unrestricted,);
五、离线加载web
WebView(onWebViewCreated: (WebViewController controller) {controller.loadLocalHtmlFile(filePath)},javascriptMode: JavascriptMode.unrestricted,);Future<void> downloadFile(String url) async {Dio dio = Dio();String path = (await getApplicationDocumentsDirectory()).path;String fileName=url.substring(url.lastIndexOf("/")+1);await Dio().download(url, '$path/$fileName',onReceiveProgress: (rec,total){progress = ((rec/total) * 100).floor();if(progress >= 100){filePath= '$path/$fileName';}setState(() {});});}

下面的[1、2、3]操作具体可以参考:
https://gitlab.com/NewFish/flutter_webview_test
1、工具使用
path_provider 统一路径
archive 文件解压
webview_flutter web资源解析
dio 网络框架
2、自定义插件 - webview_flutter(修复ios解析本地资源)
webview_flutter:git:url: https://gitee.com/oldBen/plugins.gitpath: packages/webview_flutterref: webview
3、扩展
https://inappwebview.dev/docs/javascript/communication/
https://cloud.tencent.com/developer/news/667143
Inappwebview Demo:
flu_web.zip
