前期准备
定义交互规范
首先确定好js与App交互规范,方便与前端开发与App开发沟通
我们定义jsBridgeName = NativeBridge
js调用app
| 方法名 | 参数(json字符串) | 返回值(json字符串) |
|---|---|---|
| getAppInfo | 无 | { "info": "AppInfo"} |
| appAlert | {"text":"appAlert"} | 无 |
app调用js
| 方法名 | 参数(json字符串) | 返回值(json字符串) |
|---|---|---|
| getJsInfo | 无 | { "info": "JsInfo"} |
| jsAlert | {"text":"jsAlert"} | 无 |
前端处理
编写一份bridge.js代码,供网页使用
function appAlert() {
window.NativeBridge.appAlert(JSON.stringify({"text": "appAlert"}));
}
function getAppInfo() {
window.NativeBridge.getAppInfo().then(result => {
alert(result);
});
}
function jsAlert(params) {
console.log(typeof params)
alert(params);
}
function getJsInfo() {
return JSON.stringify({
"info": "JsInfo",
})
}
网页使用h5.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width" initial-scale="1"/>
<title>Title</title>
<script src="bridge.js"></script>
</head>
<body>
<button onclick="appAlert()">appAlert</button>
<button onclick="getAppInfo()">getAppInfo</button>
</body>
</html>
App端处理
解决InAppWebView不支持自定义JSBridgeName
问题
查阅文档发现,InAppWebView 不支持自定义JSBridgeName,我们无法使用window.JSBridgeName.method这样的方法调用app方法
<script>
window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
window.flutter_inappwebview.callHandler('handlerFoo')
.then(function(result) {
// print to the console the data coming
// from the Flutter side.
console.log(JSON.stringify(result));
window.flutter_inappwebview
.callHandler('handlerFooWithArgs', 1, true, ['bar', 5], {foo: 'baz'}, result);
});
});
</script>
_webViewController.addJavaScriptHandler(handlerName:'handlerFoo', callback: (args) {
// return data to JavaScript side!
return {
'bar': 'bar_value', 'baz': 'baz_value'
};
});
解决
我们只需要将window.NativeBridge中的方法指向window.flutter_inappwebview.callHandler("NativeBridge", 'appAlert', ...args),注入如下js代码即可
window.NativeBridge = {
appAlert: (...args) => window.flutter_inappwebview.callHandler("NativeBridge", 'appAlert', ...args),
getAppInfo: (...args) => window.flutter_inappwebview.callHandler("NativeBridge", 'getAppInfo', ...args),
}
在flutter中,我们注入上面js代码
InAppWebView(
initialUserScripts: UnmodifiableListView<UserScript>([
UserScript(
source: getInjectJS(bridgeName, JsCallAppMethod.values.map((e) => e.name).toList()),
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START,
),
]),
)
///flutter_inappwebview默认只有window.flutter_inappwebview.callHandler方法,不能自定义
///
///这里做一个中转注入,即可使用window.youName.youMethod(args)
///
///生成代码如下
///
///window.NativeBridge = {
/// appAlert: (...args) => window.flutter_inappwebview.callHandler("NativeBridge", 'appAlert', ...args),
/// getAppInfo: (...args) => window.flutter_inappwebview.callHandler("NativeBridge", 'getAppInfo', ...args),
///}
///
String getInjectJS(String bridgeName, List<String> methodNames) {
final js = StringBuffer();
js.write("window.$bridgeName = {");
for (var name in methodNames) {
js.write('$name: (...args) => window.flutter_inappwebview.callHandler("$bridgeName", "$name", ...args),');
}
js.write("}");
return js.toString();
}
交互方法
为方便后续的处理,我们使用enum对交互方法做规范化处理
enum JsCallAppMethod {
getAppInfo,
appAlert,
;
static JsCallAppMethod? parse(String name) {
try {
return JsCallAppMethod.values.firstWhere((e) => e.name == name);
} catch (e) {
return null;
}
}
}
enum AppCallJsMethod {
getJsInfo,
jsAlert,
;
}
定义App端的交互方法
void onWebViewCreated(InAppWebViewController controller) {
this.controller = controller;
InAppWebViewController.setWebContentsDebuggingEnabled(true);
controller.loadFile(assetFilePath: Assets.htmlH5);
controller.addJavaScriptHandler(
handlerName: bridgeName,
callback: (List<dynamic> arguments) {
//这里做了处理,返回的结果为[methodName,...args]
if (arguments.isNotEmpty) {
var method = arguments[0];
Map<String, dynamic>? param;
if (arguments.length > 1) {
param = jsonDecode(arguments[1]);
}
if (method is String) {
var parse = JsCallAppMethod.parse(method);
if (parse == null) {
//未实现对应方法
} else {
return jsCallApp(method: parse, params: param);
}
}
}
},
);
}
jsCallApp({required JsCallAppMethod method, Map<String, dynamic>? params}) {
switch (method) {
case JsCallAppMethod.getAppInfo:
return jsonEncode({
"info": "AppInfo",
});
case JsCallAppMethod.appAlert:
alert(params);
break;
}
}
Future<dynamic> appCallJs({required AppCallJsMethod method, Map<String, dynamic>? params}) {
String json = jsonEncode(params);
var js = "window.${method.name}('$json')";
return controller.evaluateJavascript(source: js);
}
调试验证
webview调试
为方便进行调试和验证,开启webview调试
InAppWebViewController.setWebContentsDebuggingEnabled(true);
打开Chorme,输入网址chrome://inspect/#devices,点击inspect进入调试页面,我们可以直接调试App内的网页了,可以在控制台查看日志信息和编写代码