目前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.git
path: packages/webview_flutter
ref: 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_inappwebview
function 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的调用和传参arg
return '回调web';
});
},
2、原生调用web、传参、回调
html代码:
<script>
//被flutter调用的方法
function onFlutterCall(result){
console.log(result, typeof result);
alert(result);
return result;
}
<script>
flutter代码:
//webview_flutter
WebView(
...
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();
@override
void 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_name
ui.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--------------------
//dart
js.JsObject _connector; // 声明
//保存链接window
js.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)
}
另一种方法:(不用注册关联,相对简单)
//js
window.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.git
path: packages/webview_flutter
ref: webview
3、扩展
https://inappwebview.dev/docs/javascript/communication/
https://cloud.tencent.com/developer/news/667143
Inappwebview Demo:
flu_web.zip