flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge

在使用webview中,需要实现flutter与Javascript交互,在使用webview_flutter插件的时候,整理了一下webview与Javascript的交互JSBridge,具体可以查看

https://blog.csdn.net/gloryFlow/article/details/131683122

这里使用inappwebview插件来实现flutter与Javascript的交互JSBridge。

一、什么是JSBridge

JSBridge是一种实现webview与原生端的相互调用的能力。

在比较流行的JSBridge中,主要是通过拦截URL请求来达到 native 端和 webview 端相互通信的效果的。如WebviewJavascriptBridge。

那在inappwebview中有实现javascript交互的方式。在inappwebview中,可以使用JavaScript Handlers,来实现flutter端与javascript的交互。可以查看

https://blog.csdn.net/gloryFlow/article/details/133643136

二、修改JSBridge的JS端实现

在WebviewJavascriptBridge中,代码中使用iframe中,拦截url来达到webview与原生交互。那在inappwebview,我们可以直接嵌套使用JavaScript Handlers来实现交互。

我们定义WebviewJSBridgeReady

const String kWebviewJsBridgeReady = '''

window.onerror = function(err) {

log('window.onerror: ' + err)

}

function setupWebViewJavascriptBridge(callback) {

if (window.AppJSBridge) {

return callback(AppJSBridge);

} else {

document.addEventListener('AppJSBridgeReady', function() {

callback(AppJSBridge);

},false);

}

}

setupWebViewJavascriptBridge(function(bridge) {

bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {

var responseData = { 'Javascript Says':'Right back atcha!' }

responseCallback(responseData)

});

bridge.registerHandler('JSHandler', function(data, responseCallback) {

var responseData = { 'Javascript Says':'Right back atcha!' }

responseCallback(responseData)

});

}

''';

修改后的WebviewJsBridge,其中定义了sendMessageQueue、messageHandlers等。其中定义了一个window.AppJSBridge,创建了事件document.createEvent(‘Event’),初始化event.initEvent(‘AppJSBridgeReady’, true, true); 触发对象dispatch触发对象可以是任何元素或其他事件目标document.dispatchEvent(event);

const String kInAppWebViewJavascriptBridge = '''

function preprocessorJS() {

if (window.AppJSBridge) {

return;

}

if (!window.onerror) {

window.onerror = function(msg, url, line) {

console.log("AppJSBridge: ERROR:" + msg + "@" + url + ":" + line);

}

}

// var messagingIframe;

var sendMessageQueue = [];

var messageHandlers = {};

var responseCallbacks = {};

var uniqueId = 1;

var dispatchMessagesWithTimeoutSafety = true;

function registerHandler(handlerName, handler) {

messageHandlers[handlerName] = handler;

}

function callHandler(handlerName, data, responseCallback) {

if (arguments.length == 2 && typeof data == 'function') {

responseCallback = data;

data = null;

}

_doSend({ handlerName:handlerName, data:data }, responseCallback);

}

function call(handlerName, data, responseCallback) {

if (arguments.length == 2 && typeof data == 'function') {

responseCallback = data;

data = null;

}

_doSend({ handlerName:handlerName, data:data }, responseCallback);

}

function disableJavscriptAlertBoxSafetyTimeout() {

dispatchMessagesWithTimeoutSafety = false;

}

function _doSend(message, responseCallback) {

if (responseCallback) {

var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();

responseCallbacks[callbackId] = responseCallback;

message['callbackId'] = callbackId;

}

sendMessageQueue.push(message);

// 通过使用inappwebview的callHandler

window.flutter_inappwebview.callHandler(message['handlerName'], message);

}

function _fetchQueue() {

var messageQueueString = JSON.stringify(sendMessageQueue);

sendMessageQueue = [];

return messageQueueString;

}

function _dispatchMessageFromObjC(messageJSON) {

if (dispatchMessagesWithTimeoutSafety) {

setTimeout(_doDispatchMessageFromObjC);

} else {

_doDispatchMessageFromObjC();

}

// 打印log

_consoleLog("AppJSBridge: messageJSON:" + messageJSON);

function _doDispatchMessageFromObjC() {

var message = JSON.parse(messageJSON);

var messageHandler;

var responseCallback;

if (message.responseId) {

responseCallback = responseCallbacks[message.responseId];

if (!responseCallback) {

return;

}

responseCallback(message.responseData);

delete responseCallbacks[message.responseId];

} else {

if (message.callbackId) {

var callbackResponseId = message.callbackId;

responseCallback = function(responseData) {

_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });

};

}

var handler = messageHandlers[message.handlerName];

if (!handler) {

_consoleLog("AppJSBridge: WARNING: no handler for message from ObjC:", message);

} else {

handler(message.data, responseCallback);

}

}

}

}

function _handleMessageFromObjC(messageJSON) {

_dispatchMessageFromObjC(messageJSON);

}

registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);

window.AppJSBridge = {

registerHandler: registerHandler,

callHandler: callHandler,

call: call,

disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,

_fetchQueue: _fetchQueue,

_handleMessageFromObjC: _handleMessageFromObjC,

_consoleLog: _consoleLog,

};

// 打印log

function _consoleLog(message) {

// 显示来自flutter的回调

var logJSON = { 'message':message, 'logType':1 }

callHandler("log", JSON.stringify(logJSON));

}

window.WeixinJSBridge = window.AppJSBridge;

// 创建事件

var event = document.createEvent('Event');

// 定义事件名为'build'.

event.initEvent('AppJSBridgeReady', true, true);

event.bridge = window.AppJSBridge;

// 触发对象可以是任何元素或其他事件目标

document.dispatchEvent(event);

}

function preprocessorReadyJs() {

if (!window.flutter_inappwebview.callHandler) {

window.flutter_inappwebview.callHandler = function () {

var _callHandlerID = setTimeout(function () { });

window.flutter_inappwebview._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));

return new Promise(function (resolve, reject) {

window.flutter_inappwebview[_callHandlerID] = resolve;

});

};

}

preprocessorJS();

}

preprocessorReadyJs()

''';

三、在flutter端使用InAppWebViewController实现调用方法的InAppWebJSHandlerManager

在flutter端使用InAppWebViewController实现调用方法,使用InAppWebViewController来调用evaluateJavascript来调用js的方法。我们可以在flutter端实现调用flutter_inappwebview插件的JavaScript Handlers。这里我定义了消息队列,定义了消息的JSMessage,与responseCallbacks回调。

// flutter_inappwebview插件的JavaScript Handlers

class InAppWebJSHandlerManager {

// flutter_inappwebview插件

InAppWebViewController? inAppWebViewController;

BuildContext? context;

// 存储的消息messageHandler

Map messageHandlers = {};

// 存储的回调callback, responseCallback

Map responseCallbacks = {};

// 开启的消息队列,发送的消息均会存储到该队列中

List? startupMessageQueue = [];

// 消息的标识

int _uniqueId = 0;

JSChannelManager() {

// TODO: implement JSChannelManager

}

void updateController({

required BuildContext context,

InAppWebViewController? inAppWebViewController,

}) {

this.context = context;

this.inAppWebViewController = inAppWebViewController;

}

// 处理消息队列

void flutterFlushMessageQueue() async {

// 获取h5发送的列表

// 处理H5存的消息队列发送的MessageQueue

String? messageQueueString;

if (inAppWebViewController != null) {

messageQueueString = await inAppWebViewController

?.evaluateJavascript(source: webViewJavascriptFetchQueyCommand());

}

LoggerManager().debug("flutterFlushMessageQueue:${messageQueueString}");

flushMessageQueue(messageQueueString);

}

// 处理来自H5的消息列表

void flushMessageQueue(String? messageQueueString) {

if (!(messageQueueString != null && messageQueueString.isNotEmpty)) {

return;

}

dynamic? aFromH5Messages = jsonDecode(messageQueueString);

if (aFromH5Messages != null && aFromH5Messages is List) {

for (dynamic aMsgJson in aFromH5Messages) {

if (aMsgJson is Map) {

JSMessage jsMessage = JSMessage.fromJson(aMsgJson);

LoggerManager().debug(

"flushMessageQueue aFromH5Messages aMsgJson:${aMsgJson} jsMessage:${jsMessage}");

// 从H5获取或者接收到的消息,如果responseId不为空,则为flutter调用H5方法,H5给flutter的回调

if (jsMessage.responseId != null &&

jsMessage.responseId!.isNotEmpty) {

// 如果responseId不为空,则为flutter调用H5方法,H5给flutter的回调

ResponseCallback? responseCallback =

responseCallbacks[jsMessage.responseId];

if (responseCallback != null) {

// 处理H5返回给flutter的回调

responseCallback(jsMessage.responseData);

}

} else {

ResponseCallback? responseCallback;

// 如果responseId为空时候,则是来自H5发送的flutter的消息

// 获取H5传过来的标识callbackId

String? callbackId = jsMessage.callbackId;

if (callbackId != null && callbackId.isNotEmpty) {

// 接收到来自H5的消息

JSMessage aMessage = JSMessage();

aMessage.copy(aNewMessage: aMessage, aOldMessage: jsMessage);

responseCallback = (dynamic responseData) {

// flutter回调给H5

// 将H5传过来的callbackId作为responseId回调传递给H5

aMessage.responseId = callbackId;

aMessage.responseData = responseData;

_queueMessage(aMessage);

};

} else {

responseCallback = (dynamic responseData) {

// callbackId为空,不做任何处理

};

}

// 从flutter已经注册Register方法中找出对应的方法

JSBridgeHandler? jsBridgeHandler =

messageHandlers[jsMessage.handlerName];

if (jsBridgeHandler != null) {

// 在flutter该handlerName的方法已经注册register

jsBridgeHandler(jsMessage.data, responseCallback);

} else {

// 在flutter该handlerName没有注册,则不做任何处理

}

}

}

}

}

}

// 处理从H5收到的消息

void _dispatchMessage(JSMessage message) async {

String messageJSON = jsonEncode(message.toJson());

messageJSON = messageJSON.replaceAll("\\", "\\\\");

messageJSON = messageJSON.replaceAll("\"", "\\\"");

messageJSON = messageJSON.replaceAll("\'", "\\\'");

messageJSON = messageJSON.replaceAll("\n", "\\n");

messageJSON = messageJSON.replaceAll("\r", "\\r");

messageJSON = messageJSON.replaceAll("\f", "\\f");

messageJSON = messageJSON.replaceAll("\u2028", "\\u2028");

messageJSON = messageJSON.replaceAll("\u2029", "\\u2029");

String javascriptCommand =

webViewJavascriptHandleMessageFromObjCCommand(messageJSON);

if (inAppWebViewController != null) {

await inAppWebViewController?.evaluateJavascript(source: javascriptCommand);

}

}

// 注入js

void injectJavascript(String javascript) async {

if (inAppWebViewController != null) {

await inAppWebViewController?.evaluateJavascript(source: javascript);

}

}

// 注入js

void injectJavascriptReady() async {

if (inAppWebViewController != null) {

await inAppWebViewController?.evaluateJavascript(source: '$kWebviewJsBridgeReady');

}

}

// 注入js

void injectBridgeJavascript() async {

if (inAppWebViewController != null) {

await inAppWebViewController?.evaluateJavascript(source: '$kInAppWebViewJavascriptBridge');

}

LoggerManager().debug("injectJavascript");

// 处理flutter发送的消息队列

if (startupMessageQueue != null && startupMessageQueue!.isNotEmpty) {

List tmpList = startupMessageQueue!;

startupMessageQueue = null;

for (JSMessage message in tmpList) {

_dispatchMessage(message);

}

}

}

// 向H5发送消息

void _sendData(String handleName,

{dynamic? data, ResponseCallback? responseCallback}) {

String callbackId = "flutter_cb_${++_uniqueId}";

JSMessage jsMessage = JSMessage();

jsMessage.callbackId = callbackId;

jsMessage.handlerName = handleName;

jsMessage.data = data;

// 将callbackId存储到responseCallbacks中,callbackId会被H5通过responseId返回

if (responseCallback != null) {

responseCallbacks[callbackId] = responseCallback;

}

_queueMessage(jsMessage);

}

// 将发送给H5的消息存到startupMessageQueue中

void _queueMessage(JSMessage jsMessage) {

if (startupMessageQueue != null) {

startupMessageQueue!.add(jsMessage);

}

_dispatchMessage(jsMessage);

}

// 注入js的command

String webViewJavascriptCheckCommand() {

return "typeof window.AppJSBridge == \'object\';";

}

String webViewJavascriptFetchQueyCommand() {

return "AppJSBridge._fetchQueue();";

}

String webViewJavascriptHandleMessageFromObjCCommand(String messageJSON) {

return "AppJSBridge._handleMessageFromObjC('${messageJSON}');";

}

// 判断AppJSBridge

Future checkJavascriptBridge() async {

String? result;

if (inAppWebViewController != null) {

bool jsBridge = await inAppWebViewController

?.evaluateJavascript(source: webViewJavascriptCheckCommand());

result = (jsBridge?"true":"false");

}

LoggerManager().debug("checkJavascriptBridge result:${result}");

return result;

}

/// flutter开放出去的方法,flutter调用H5方法统一使用该callHandler

/// callHandler

void callHandler(String handleName,

{dynamic? data, ResponseCallback? responseCallback}) {

if (handleName.isNotEmpty) {

_sendData(handleName, data: data, responseCallback: responseCallback);

}

}

/// flutter注册方法

/// flutter注册方法,提供给H5调用

void registerHandler(String handlerName, JSBridgeHandler jsBridgeHandler) {

if (handlerName.isNotEmpty) {

messageHandlers[handlerName] = jsBridgeHandler;

}

}

void addJSBridgeHandlers() {

if (inAppWebViewController != null) {

messageHandlers.forEach((handlerName, jsBridgeHandler) {

inAppWebViewController?.addJavaScriptHandler(handlerName: handlerName, callback: (List arguments) {

LoggerManager().debug("inAppWebViewController.addJavaScriptHandler arguments:${arguments}");

flutterFlushMessageQueue();

});

});

}

}

// 移除注册的方法

void removeHandler(String handleName) {

if (handleName.isNotEmpty) {

messageHandlers.remove(handleName);

}

}

// 重置,将responseCallbacks、startupMessageQueue重置

void reset() {

startupMessageQueue = [];

responseCallbacks = {};

_uniqueId = 0;

}

}

InAppWebJSHandlerManager中使用inAppWebViewController?.addJavaScriptHandler来处理接收H5端的JS消息,并且将处理回调返回给H5。 当收到H5消息,flutter根据callbackId将处理后的结果回调给H5。

四、InAppWebJSBridgeRegister:appBridge调用的方法,flutter注册的方法

InAppWebJSBridgeRegister实现处理flutter注册的方法,提供相应的方法,H5端JS可以方法调用。

// appBridge调用的方法,flutter注册的方法

class InAppWebJSBridgeRegister {

late InAppWebJSHandlerManager _inAppWebJSHandlerManager;

// 支付

final ChannelPayPlatform _channelPayPlatform = ChannelPayPlatform();

// 打开app等

final ChannelLauncher _channelLauncher = ChannelLauncher();

// 弹窗

final ChannelDialog _channelDialog = ChannelDialog();

// 扫码或者识别二维码

final ChannelQrScanner _channelQrScanner = ChannelQrScanner();

InAppWebJSBridgeRegister({required InAppWebJSHandlerManager inAppWebJSHandlerManager}) {

_inAppWebJSHandlerManager = inAppWebJSHandlerManager;

}

// 注册handlers

void registerHandlers({JSChannelRegisterHandler? jsChannelRegisterHandler}) {

// 设置标题

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.setTitle,

(data, responseCallback) {

if (data != null && data is String) {

String title = data;

if (jsChannelRegisterHandler != null) {

jsChannelRegisterHandler(JSChannelRegisterMethod.setTitle, title);

}

}

});

// 获取用户昵称

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getUsername,

(data, responseCallback) {

UserModel userModel =

Provider.of(OneContext().context!, listen: false);

String userNickName = userModel.userNickName ?? "";

if (responseCallback != null) {

responseCallback(userNickName);

}

});

// 获取定位

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getLoc,

(data, responseCallback) {

// TODO 获取定位

});

// 获取App名称

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAppName,

(data, responseCallback) {

PackageInfo.fromPlatform().then((packageInfo) {

String appName = "${packageInfo.appName}";

if (responseCallback != null) {

responseCallback(appName);

}

});

});

// 获取版本号

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getVersion,

(data, responseCallback) {

PackageInfo.fromPlatform().then((packageInfo) {

String version = "${packageInfo.buildNumber}";

String versionCode = version.replaceAll(".", "");

if (responseCallback != null) {

responseCallback(versionCode);

}

});

});

// 获取用户id

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getUserId,

(data, responseCallback) {

UserModel userModel =

Provider.of(OneContext().context!, listen: false);

String userId = userModel.userId ?? "";

if (responseCallback != null) {

responseCallback(userId);

}

});

// 获取用户登录认证token

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAuthorization,

(data, responseCallback) {

UserModel userModel =

Provider.of(OneContext().context!, listen: false);

String token = userModel.token ?? "";

if (responseCallback != null) {

responseCallback(token);

}

});

// 调用支付(微信支付/支付宝支付)原生

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.setPayPlatform,

(data, responseCallback) {

_channelPayPlatform.openUniPay(data, responseCallback);

});

// 打开扫一扫

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.openScan,

(data, responseCallback) {

// 打开扫一扫界面

_channelQrScanner.openScanner(

JSChannelRegisterMethod.openScan, data, responseCallback);

});

// 打开扫一扫

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.scanQrCode,

(data, responseCallback) {

// 打开扫一扫界面

_channelQrScanner.openScanner(

JSChannelRegisterMethod.scanQrCode, data, responseCallback);

});

// 打系统电话

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.callTelPhone,

(data, responseCallback) {

_channelLauncher.openLauncher(

JSChannelRegisterMethod.callTelPhone, data, responseCallback);

});

// 发送短信

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.sendSms,

(data, responseCallback) {

_channelLauncher.openLauncher(

JSChannelRegisterMethod.sendSms, data, responseCallback);

});

// 对话框 showDialog

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.showDialog,

(data, responseCallback) {

_channelDialog.openShowDialog(data, responseCallback);

});

// 底部选择框

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.showCheckBox,

(data, responseCallback) {

_channelDialog.openShowSheetBox(data, responseCallback);

});

// 保存图片到相册

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.saveImage,

(data, responseCallback) {

// 保存图片到相册

if (data != null && data is String && data.isNotEmpty) {

FlutterLoadingHud.showLoading(message: "保存中...");

SaveToAlbumUtil.saveImage(data, onCallback: (bool result, String message) {

FlutterLoadingHud.dismiss();

if (result) {

// 保存成功

FlutterLoadingHud.showToast(message: message);

} else {

// 保存失败

FlutterLoadingHud.showToast(message: message);

}

});

}

});

// 识别二维码

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.detectorQRCode,

(data, responseCallback) {

// 识别图片中的二维码

_channelQrScanner.openScanner(

JSChannelRegisterMethod.detectorQRCode, data, responseCallback);

});

// 打开App

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.openApp,

(data, responseCallback) {

_channelLauncher.openLauncher(

JSChannelRegisterMethod.openApp, data, responseCallback);

});

// log

_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.log,

(data, responseCallback) {

Map dataJson = jsonDecode(data);

int loggerType = dataJson["logType"];

String message = dataJson["message"];

if (LoggerMode.debug == loggerType) {

LoggerManager().debug("registerHandlers log data: ${message}");

} else if (LoggerMode.verbose == loggerType) {

LoggerManager().verbose("registerHandlers log data: ${message}");

} else if (LoggerMode.info == loggerType) {

LoggerManager().info("registerHandlers log data: ${message}");

} else if (LoggerMode.warning == loggerType) {

LoggerManager().warning("registerHandlers log data: ${message}");

} else if (LoggerMode.error == loggerType) {

LoggerManager().error("registerHandlers log data: ${message}");

}

});

_inAppWebJSHandlerManager.addJSBridgeHandlers();

}

// 处理是否跳转,true可跳转,false不可跳转

bool shouldOverrideUrlLoading(Uri uri) {

///在页面跳转之前调用 isForMainFrame为false,页面不跳转.导致网页内很多链接点击没效果

String url = uri.toString();

return webViewNavigationAllowed(url);

}

// 处理是否跳转,true可跳转,false不可跳转

bool webViewNavigationAllowed(String url) {

LoggerManager().debug('navigationDelegate decode $url');

String telPrefix = "tel://";

String smsPrefix = "sms://";

String appPrefix = "app://";

if (url.startsWith(telPrefix)) {

String data = url.substring(telPrefix.length);

_channelLauncher.openLauncher(

JSChannelRegisterMethod.callTelPhone, data, null);

// 不可跳转

return false;

}

if (url.startsWith(smsPrefix)) {

String data = url.substring(smsPrefix.length);

_channelLauncher.openLauncher(

JSChannelRegisterMethod.sendSms, data, null);

// 不可跳转

return false;

}

if (url.startsWith(appPrefix)) {

// app://close

_channelLauncher.openappUrl(url);

return false;

}

if (url == "about:blank") {

// 空页面进行跳转

return true;

}

// 可跳转

return true;

}

}

五、定义JSMessage:H5与flutter交互的消息体

class JSMessage {

// {handlerName: getSessionID, data: , callbackId: cb_2_1665631238605}

// handlerName

String? handlerName;

// data

// flutter发送给H5的data,参数

dynamic? data;

/// callbackId,

/// H5发送给flutter的callbackId,

/// flutter处理后将调用 AppJSBridge._handleMessageFromObjC('%@');

/// H5从responseCallbacks中根据callbackId找到callback回调方法进行执行

String? callbackId;

/// responseId

/// flutter发送给H5的responseId,

/// responseId和callbackId是一样的

/// 如果是H5调用flutter时候,从H5过来的callbackId作为responseId回调给H5

/// 如果是flutter调用H5,从flutter过来的callbackId作为responseId回调给flutter

String? responseId;

/// 回调的数据

/// 如果是H5调用flutter时候,从flutter传给H5的responseData作为回调数据

/// 如果是flutter调用H5,从H5传给flutter的responseData作为回调数据

dynamic? responseData;

JSMessage();

JSMessage.fromJson(Map json) {

callbackId = json['callbackId'];

data = json['data'];

handlerName = json['handlerName'];

responseId = json['responseId'];

responseData = json['responseData'];

}

Map toJson() {

final Map data = new Map();

data['callbackId'] = this.callbackId;

data["data"] = this.data;

data["handlerName"] = this.handlerName;

data['responseId'] = this.responseId;

data['responseData'] = this.responseData;

return data;

}

void copy({required JSMessage aNewMessage, required JSMessage aOldMessage}) {

aNewMessage.callbackId = aOldMessage.callbackId;

aNewMessage.data = aOldMessage.data;

aNewMessage.handlerName = aOldMessage.handlerName;

aNewMessage.responseId = aOldMessage.responseId;

aNewMessage.responseData = aOldMessage.responseData;

}

}

六、封装inwebview中使用InAppWebJSBridgeRegister、InAppWebJSHandlerManager

我们封装inwebview中,使用InAppWebJSBridgeRegister、InAppWebJSHandlerManager。

定义InAppWebJSHandlerManager

// JS与Flutter调用的message Queue

final InAppWebJSHandlerManager _inAppWebJSHandlerManager =

InAppWebJSHandlerManager();

定义InAppWebJSBridgeRegister

// flutter注册供H5调用的方法

late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;

_inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(

inAppWebJSHandlerManager: _inAppWebJSHandlerManager);

我们在onWebViewCreated中,可以得到InAppWebViewController,更新_inAppWebJSHandlerManager的controller

_inAppWebJSHandlerManager.updateController(

context: context,

inAppWebViewController: webViewController,

);

在onWebViewCreated回调中注入ready代码

// 注入jsReady

_inAppWebJSHandlerManager.injectJavascriptReady();

在onWebViewCreated回调中注册flutter端的方法,

// register a JavaScript handler with name "myHandlerName"

_inAppWebJSBridgeRegister.registerHandlers(

jsChannelRegisterHandler: (handlerName, data) {

if (JSChannelRegisterMethod.setTitle == handlerName) {

setWebPageTitle(data);

}

});

在onLoadStop回调中注入kInAppWebViewJavascriptBridge

onLoadStop: (controller, url) async {

// 注入

_inAppWebJSHandlerManager.injectBridgeJavascript();

_inAppWebJSHandlerManager.checkJavascriptBridge();

// 加载完成

widget.onLoadFinished(url.toString());

},

完整代码如下

class WebViewInAppScreen extends StatefulWidget {

const WebViewInAppScreen({

Key? key,

required this.url,

this.onWebProgress,

this.onWebResourceError,

required this.onLoadFinished,

required this.onWebTitleLoaded,

this.onWebViewCreated,

}) : super(key: key);

final String url;

final Function(int progress)? onWebProgress;

final Function(String? errorMessage)? onWebResourceError;

final Function(String? url) onLoadFinished;

final Function(String? webTitle)? onWebTitleLoaded;

final Function(InAppWebViewController controller)? onWebViewCreated;

@override

State createState() => _WebViewInAppScreenState();

}

class _WebViewInAppScreenState extends State {

final GlobalKey webViewKey = GlobalKey();

InAppWebViewController? webViewController;

InAppWebViewGroupOptions options = InAppWebViewGroupOptions(

crossPlatform: InAppWebViewOptions(

useShouldOverrideUrlLoading: true,

mediaPlaybackRequiresUserGesture: false,

applicationNameForUserAgent: "app-webview",

),

android: AndroidInAppWebViewOptions(

useHybridComposition: true,

),

ios: IOSInAppWebViewOptions(

allowsInlineMediaPlayback: true,

),

);

// JS与Flutter调用的message Queue

final InAppWebJSHandlerManager _inAppWebJSHandlerManager =

InAppWebJSHandlerManager();

// cookie

final InAppWebJSCookieConfig _inAppWebViewJSCookieConfig =

InAppWebJSCookieConfig();

// flutter注册供H5调用的方法

late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;

bool _isDisposed = false;

@override

void initState() {

// TODO: implement initState

super.initState();

_isDisposed = false;

_inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(

inAppWebJSHandlerManager: _inAppWebJSHandlerManager);

}

@override

void dispose() {

// TODO: implement dispose

_isDisposed = true;

_inAppWebJSHandlerManager.reset();

webViewController?.clearCache();

// _inAppWebViewJSCookieConfig.clear();

super.dispose();

}

// 设置页面标题

void setWebPageTitle(data) {

if (widget.onWebTitleLoaded != null) {

widget.onWebTitleLoaded!(data);

}

}

// flutter调用H5方法

void callJSMethod() {

_inAppWebJSHandlerManager.callHandler("JSAPPHandler",

data: {"id": "a18c9fe0d"}, responseCallback: (dynamic responseData) {

LoggerManager().debug("callJSMethod responseData:${responseData}");

FlutterLoadingHud.showToast(message: jsonEncode(responseData));

});

}

@override

Widget build(BuildContext context) {

return Column(

children: [

Expanded(

child: InAppWebView(

key: webViewKey,

initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),

initialUserScripts: UnmodifiableListView([

UserScript(

source:

"document.cookie='token=${ApiAuth.getToken()};domain='.inice.cn';path=/'",

injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START),

]),

initialOptions: options,

onWebViewCreated: (controller) {

webViewController = controller;

_inAppWebViewJSCookieConfig.setCookie(widget.url);

_inAppWebJSHandlerManager.updateController(

context: context,

inAppWebViewController: webViewController,

);

// 注入jsReady

_inAppWebJSHandlerManager.injectJavascriptReady();

// register a JavaScript handler with name "myHandlerName"

_inAppWebJSBridgeRegister.registerHandlers(

jsChannelRegisterHandler: (handlerName, data) {

if (JSChannelRegisterMethod.setTitle == handlerName) {

setWebPageTitle(data);

}

});

String filePre = "file://";

if (widget.url.startsWith(filePre)) {

String html = widget.url.substring(filePre.length);

webViewController?.loadFile(

assetFilePath: 'assets/htmls/${html}');

} else {

if (widget.url.startsWith("http://") ||

widget.url.startsWith("https://")) {

webViewController?.loadUrl(

urlRequest: URLRequest(url: Uri.parse(widget.url)));

}

}

if (widget.onWebViewCreated != null) {

widget.onWebViewCreated!(controller);

}

},

onTitleChanged: (controller, title) {

if (widget.onWebTitleLoaded != null) {

widget.onWebTitleLoaded!(title);

}

},

onLoadStart: (controller, url) {},

androidOnPermissionRequest: (controller, origin, resources) async {

return PermissionRequestResponse(

resources: resources,

action: PermissionRequestResponseAction.GRANT);

},

shouldOverrideUrlLoading: (controller, navigationAction) async {

var uri = navigationAction.request.url!;

bool canNavigate =

_inAppWebJSBridgeRegister.shouldOverrideUrlLoading(uri);

// 允许路由替换

return canNavigate

? NavigationActionPolicy.ALLOW

: NavigationActionPolicy.CANCEL;

},

onLoadStop: (controller, url) async {

// 注入

_inAppWebJSHandlerManager.injectBridgeJavascript();

_inAppWebJSHandlerManager.checkJavascriptBridge();

// 加载完成

widget.onLoadFinished(url.toString());

},

onProgressChanged: (controller, progress) {

if (widget.onWebProgress != null) {

widget.onWebProgress!(progress);

}

},

onLoadError: (controller, Uri? url, int code, String message) {

if (widget.onWebResourceError != null) {

widget.onWebResourceError!(message);

}

},

onUpdateVisitedHistory: (controller, url, androidIsReload) {

print("onUpdateVisitedHistory:${url}");

},

onConsoleMessage: (controller, consoleMessage) {

print("consoleMessage:${consoleMessage}");

},

),

),

Container(

height: ScreenUtil().bottomBarHeight + 50.0,

color: Colors.white,

child: Column(

children: [

Expanded(

child: Row(

mainAxisAlignment: MainAxisAlignment.center,

crossAxisAlignment: CrossAxisAlignment.center,

children: [

ElevatedButton(

child: Icon(Icons.arrow_back),

onPressed: () {

webViewController?.goBack();

},

),

SizedBox(

width: 25.0,

),

ElevatedButton(

child: Icon(Icons.arrow_forward),

onPressed: () {

webViewController?.goForward();

},

),

SizedBox(

width: 25.0,

),

ElevatedButton(

child: Icon(Icons.refresh),

onPressed: () {

// callJSMethod();

webViewController?.reload();

},

),

],

),

),

Container(

height: ScreenUtil().bottomBarHeight,

),

],

),

),

],

);

}

}

七、使用inappwebview的page

最后,我们使用inappwebview,使用一个页面打开对应的需要的链接地址,这里使用的本地测试页面

class InAppWebViewPage extends StatefulWidget {

const InAppWebViewPage({Key? key, this.arguments}) : super(key: key);

final Object? arguments;

@override

State createState() => _InAppWebViewPageState();

}

class _InAppWebViewPageState extends State {

String title = "";

String? url;

double webProgress = 0.0;

@override

void initState() {

// TODO: implement initState

if (widget.arguments != null && widget.arguments is Map) {

Map obj = widget.arguments as Map;

url = obj["url"];

}

LoggerManager().debug("_WebViewPageState arguments:${widget.arguments}");

LoggerManager().debug("_WebViewPageState url:${url}");

super.initState();

}

@override

void dispose() {

// TODO: implement dispose

super.dispose();

}

@override

Widget build(BuildContext context) {

return Scaffold(

resizeToAvoidBottomInset: false,

appBar: WebAppBar(

toolbarHeight: 44.0,

backgroundColor: Theme.of(context).primaryColor,

centerWidget: Text(

"${title}",

textAlign: TextAlign.center,

overflow: TextOverflow.ellipsis,

style: TextStyle(

fontSize: 17,

color: ColorUtil.hexColor(0xffffff),

fontWeight: FontWeight.w600,

fontStyle: FontStyle.normal,

decoration: TextDecoration.none,

),

),

leadingWidget: Row(

mainAxisAlignment: MainAxisAlignment.start,

crossAxisAlignment: CrossAxisAlignment.center,

children: [

IconButton(

padding: EdgeInsets.all(0.0),

onPressed: () {

navigatorBack();

},

icon: Icon(

Icons.close_rounded,

color: Colors.white,

size: 30.0,

),

),

],

),

trailingWidget: Row(

mainAxisAlignment: MainAxisAlignment.end,

crossAxisAlignment: CrossAxisAlignment.center,

children: [

SizedBox(

width: 28.0,

),

SizedBox(

width: 28.0,

),

],

),

),

body: Stack(

children: [

WebViewInAppScreen(

url: url ?? "",

onWebProgress: (int progress) {

if (mounted) {

// TODO onWebProgress

double precent = progress / 100.0;

if (precent > 1.0) {

precent = 1.0;

}

if (precent < 0.0) {

precent = 0.0;

}

setState(() {

webProgress = precent;

LoggerManager().debug("webProgress:${webProgress}");

});

}

},

onLoadFinished: (String? url) {

if (mounted) {

// TODO onLoadFinished

}

},

onWebTitleLoaded: (String? webTitle) {

if (mounted) {

setState(() {

title = webTitle ?? "";

});

}

},

),

buildProgressIndicator(context),

],

),

);

}

Widget buildProgressIndicator(BuildContext context) {

return (webProgress != 1.0)

? LinearProgressIndicator(

backgroundColor: Colors.transparent,

valueColor: AlwaysStoppedAnimation(ColorUtil.hexColor(0x3b93ff)),

value: webProgress,

minHeight: 2,

)

: Container();

}

void navigatorBack() {

NavigatorPageRouter.pop();

}

}

到此,inappwebview实现flutter与Javascript的交互JSBridge基本内容已经完成了。 查看效果

八、小结

inappwebview实现flutter与Javascript的交互JSBridge。描述可能不是特别准确,请见谅。

https://blog.csdn.net/gloryFlow/article/details/133667017

学习记录,每天不停进步。

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。