flutter開發(fā)實戰(zhàn)-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的交互??梢圆榭?/p>
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,創(chuàng)建了事件document.createEvent(‘Event’),初始化event.initEvent(‘AppJSBridgeReady’, true, true); 觸發(fā)對象dispatch觸發(fā)對象可以是任何元素或其他事件目標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;
// 創(chuàng)建事件
var event = document.createEvent('Event');
// 定義事件名為'build'.
event.initEvent('AppJSBridgeReady', true, true);
event.bridge = window.AppJSBridge;
// 觸發(fā)對象可以是任何元素或其他事件目標
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<String, dynamic> messageHandlers = {};
// 存儲的回調callback, responseCallback
Map<String, dynamic> responseCallbacks = {};
// 開啟的消息隊列,發(fā)送的消息均會存儲到該隊列中
List<JSMessage>? 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發(fā)送的列表
// 處理H5存的消息隊列發(fā)送的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<String, dynamic>) {
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發(fā)送的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發(fā)送的消息隊列
if (startupMessageQueue != null && startupMessageQueue!.isNotEmpty) {
List<JSMessage> tmpList = startupMessageQueue!;
startupMessageQueue = null;
for (JSMessage message in tmpList) {
_dispatchMessage(message);
}
}
}
// 向H5發(fā)送消息
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);
}
// 將發(fā)送給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<String?> 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方法統(tǒng)一使用該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<dynamic> 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<UserModel>(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<UserModel>(OneContext().context!, listen: false);
String userId = userModel.userId ?? "";
if (responseCallback != null) {
responseCallback(userId);
}
});
// 獲取用戶登錄認證token
_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAuthorization,
(data, responseCallback) {
UserModel userModel =
Provider.of<UserModel>(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);
});
// 打系統(tǒng)電話
_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.callTelPhone,
(data, responseCallback) {
_channelLauncher.openLauncher(
JSChannelRegisterMethod.callTelPhone, data, responseCallback);
});
// 發(fā)送短信
_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<String, dynamic> 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發(fā)送給H5的data,參數
dynamic? data;
/// callbackId,
/// H5發(fā)送給flutter的callbackId,
/// flutter處理后將調用 AppJSBridge._handleMessageFromObjC('%@');
/// H5從responseCallbacks中根據callbackId找到callback回調方法進行執(zhí)行
String? callbackId;
/// responseId
/// flutter發(fā)送給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<String, dynamic> json) {
callbackId = json['callbackId'];
data = json['data'];
handlerName = json['handlerName'];
responseId = json['responseId'];
responseData = json['responseData'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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<WebViewInAppScreen> createState() => _WebViewInAppScreenState();
}
class _WebViewInAppScreenState extends State<WebViewInAppScreen> {
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: <Widget>[
Expanded(
child: InAppWebView(
key: webViewKey,
initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),
initialUserScripts: UnmodifiableListView<UserScript>([
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: <Widget>[
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<InAppWebViewPage> createState() => _InAppWebViewPageState();
}
class _InAppWebViewPageState extends State<InAppWebViewPage> {
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文章來源:http://www.zghlxwxcb.cn/news/detail-713861.html
學習記錄,每天不停進步。文章來源地址http://www.zghlxwxcb.cn/news/detail-713861.html
到了這里,關于flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!