当前位置: 首页 > news >正文

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

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的callHandlerwindow.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,};// 打印logfunction _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;// 存储的消息messageHandlerMap<String, dynamic> messageHandlers = {};// 存储的回调callback, responseCallbackMap<String, dynamic> responseCallbacks = {};// 开启的消息队列,发送的消息均会存储到该队列中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发送的列表// 处理H5存的消息队列发送的MessageQueueString? 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发送的flutter的消息// 获取H5传过来的标识callbackIdString? 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回调传递给H5aMessage.responseId = callbackId;aMessage.responseData = responseData;_queueMessage(aMessage);};} else {responseCallback = (dynamic responseData) {// callbackId为空,不做任何处理};}// 从flutter已经注册Register方法中找出对应的方法JSBridgeHandler? jsBridgeHandler =messageHandlers[jsMessage.handlerName];if (jsBridgeHandler != null) {// 在flutter该handlerName的方法已经注册registerjsBridgeHandler(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);}}// 注入jsvoid injectJavascript(String javascript) async {if (inAppWebViewController != null) {await inAppWebViewController?.evaluateJavascript(source: javascript);}}// 注入jsvoid injectJavascriptReady() async {if (inAppWebViewController != null) {await inAppWebViewController?.evaluateJavascript(source: '$kWebviewJsBridgeReady');}}// 注入jsvoid injectBridgeJavascript() async {if (inAppWebViewController != null) {await inAppWebViewController?.evaluateJavascript(source: '$kInAppWebViewJavascriptBridge');}LoggerManager().debug("injectJavascript");// 处理flutter发送的消息队列if (startupMessageQueue != null && startupMessageQueue!.isNotEmpty) {List<JSMessage> 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的commandString webViewJavascriptCheckCommand() {return "typeof window.AppJSBridge == \'object\';";}String webViewJavascriptFetchQueyCommand() {return "AppJSBridge._fetchQueue();";}String webViewJavascriptHandleMessageFromObjCCommand(String messageJSON) {return "AppJSBridge._handleMessageFromObjC('${messageJSON}');";}// 判断AppJSBridgeFuture<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方法统一使用该callHandler/// callHandlervoid 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;}// 注册handlersvoid 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);});// 打系统电话_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<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}// handlerNameString? 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回调给flutterString? 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 Queuefinal 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;@overrideState<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 Queuefinal InAppWebJSHandlerManager _inAppWebJSHandlerManager =InAppWebJSHandlerManager();// cookiefinal InAppWebJSCookieConfig _inAppWebViewJSCookieConfig =InAppWebJSCookieConfig();// flutter注册供H5调用的方法late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;bool _isDisposed = false;@overridevoid initState() {// TODO: implement initStatesuper.initState();_isDisposed = false;_inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(inAppWebJSHandlerManager: _inAppWebJSHandlerManager);}@overridevoid 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));});}@overrideWidget 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;@overrideState<InAppWebViewPage> createState() => _InAppWebViewPageState();
}class _InAppWebViewPageState extends State<InAppWebViewPage> {String title = "";String? url;double webProgress = 0.0;@overridevoid initState() {// TODO: implement initStateif (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();}@overridevoid dispose() {// TODO: implement disposesuper.dispose();}@overrideWidget 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 onWebProgressdouble 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

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

相关文章:

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

flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge 在使用webview中&#xff0c;需要实现flutter与Javascript交互&#xff0c;在使用webview_flutter插件的时候&#xff0c;整理了一下webview与Javascript的交互JSBridge&#xff0c;具体可以查看 https:/…...

私有云盘:lamp部署nextcloud+高可用集群

目录 一、实验准备&#xff1a; 二、配置mariadb主从复制 三台主机下载mariadb 1&#xff09;主的操作 2&#xff09;从的操作 3&#xff09;测试数据是否同步 三、配置nfs让web服务挂载 1、安装 2、配置nfs服务器 3、配置web服务的httpd 4、测试 四、web 服务器 配…...

在线制作课程表

失业在家&#xff0c;开启一天一个应用的创作节奏&#xff0c;最近学了uniapp&#xff0c;特别想做点啥&#xff0c;正好家里小孩子要打印课程表&#xff0c;而且课程表还有调课的需求&#xff0c;就寻思做一个方便大家&#xff0c;到目前位置服务完全免费的&#xff0c;新鲜上…...

聊聊分布式架构06——[NIO入门]简单的Netty NIO示例

目录 Java NIO和Netty NIO比较 Java NIO&#xff1a; Netty&#xff1a; Netty NIO中的主要模块 Transport&#xff08;传输层&#xff09; Buffer&#xff08;缓冲区&#xff09; Codec&#xff08;编解码器&#xff09; Handler&#xff08;处理器&#xff09; Even…...

H5逆向之远程RPC

引言前一讲说过H5 怎么去抓包,逆向分析。其中说到RPC。这一节详细讲一下。有一种情况,JS 比较复杂,混淆的厉害。 这个时候就用到RPC。原理就是,hook web 浏览器,直接调用js 里边的方法。 Node 服务。为什么用到Node 服务,先来看下这架构 Node 对外提供各种接口,外部可以…...

解决Ubuntu18.04安装好搜狗输入法后无法打出中文的问题

首先下载安装 搜狗拼音输入法 &#xff0c;下载选择&#xff1a; x86_64 在ubuntu中设置 fcitx 最后发现安装好了&#xff0c;图标有了 &#xff0c;但是使用时不能输入中文&#xff0c;使用下面的命令解决&#xff1a; sudo apt install libqt5qml5 libqt5quick5 libqt5qu…...

Ubuntu LabelMe AI 识别

1.创建虚拟环境 conda create -n labelme python3.9 2.激活虚拟环境 conda activate labelme 3.安装labelme pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install pillow -i https://pypi.tuna.tsinghua.edu.cn/simple pip install labelme -i ht…...

基于FPGA的图像缩小算法实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将FPGA的处理结果导出到matlab中显示图像效果&#xff1a; 2.算法运行软件版本 vivado2019.2 matlab2022a 3.部分核心程序 timescale 1ns / 1p…...

黑马店评-04缓存更新策略,保证MySQL数据库中的数据和Redis中缓存的数据一致性

缓存更新策略(数据一致) 更新策略 缓存更新是Redis为了节约内存而设计出来的机制,当我们向Redis插入太多数据时就会导致缓存中的数据过多,所以Redis会对部分数据进行更新即淘汰 低一致性需求(数据长久不发生变化): 使用内存淘汰机制,例如店铺类型信息的查询缓存,因为这部分…...

matlab相机标定实验

实验原理 1. 相机标定坐标系 相机的参数对目标的识别、定位精度有很大的影响&#xff0c;相机标定就是为了求出相机的内外参数。标定中有3个不同层次的坐标系&#xff1a;世界坐标系、相机坐标系和图像坐标系&#xff08;图像物理坐标系和图像像素坐标系&#xff09;。世界坐…...

【每日一题Day343】LC2731移动机器人 | 脑筋急转弯+数学

移动机器人【LC2731】 有一些机器人分布在一条无限长的数轴上&#xff0c;他们初始坐标用一个下标从 0 开始的整数数组 nums 表示。当你给机器人下达命令时&#xff0c;它们以每秒钟一单位的速度开始移动。 给你一个字符串 s &#xff0c;每个字符按顺序分别表示每个机器人移动…...

疯狂java 1.7垃圾回收机制

内存泄漏&#xff1a;如果一些分配出去的内存得不到及时回收&#xff0c;就会引起系统运行速度下降&#xff0c;甚至导致程序瘫痪 Java程序的内存分配和回收都是由JRE在后台自动进行的。JRE会负责回收哪些不再使用的内存&#xff0c;这种机制被称为垃圾回收&#xff08;Garbag…...

day01_基础

零、今日内容 1 jdk 2 idea使用 3 HelloWorld程序 4 变量 5 数据类型 6 String 一、JDK安装 JDK java开发工具包,敲代码的环境 1.1 卸载 控制面板 -> 卸载程序 -> 选择jdk,右键卸载 1.2 安装 注意: 现在安装的是JDK8版本,虽然最新的版本是21版本,但是工作市场中最流行的…...

RabbitMQ开启消息发送确认和消费手动确认

开启RabbitMQ的生产者发送消息到RabbitMQ服务端的接收确认&#xff08;ACK&#xff09;和消费者通过手动确认或者丢弃消费的消息。 通过配置 publisher-confirm-type: correlated 和publisher-returns: true开启生产者确认消息。 server:port: 8014spring:rabbitmq:username: …...

嵌入式系统开发【深入浅出】 GPIO 类设备的驱动程序

目录 GPIO管脚的输出功能相当于控制、输入相当于检测 使用GPIO基本流程 对于某一个管脚来说最多有几种功能&#xff1f; 拓展 【定时器与系统定时器】 决定定时长短的因素: 普通定时器 系统定时器 STM32F103RBT6的时钟源有哪五种 sysclk 的时钟频率由哪个时钟源提供基…...

项目管理必备的22个公式

大家好&#xff0c;我是老原。 趁着国庆时间比较空闲&#xff0c;给你们整理了一些项目管理必备的计算公式&#xff0c;一共22个。 每一个公式都给你们标注了适用情况和使用方法&#xff0c;为了方便你们理解&#xff0c;也加了一些例子&#xff0c;保准你看了就会。 觉得不…...

ccache加速编译速度

ccache https://gitee.com/lixiaoxmm/ccache.git 依赖hiredis、zstd(zstd的cmakelists.txt在build/cmake目录下) 下载mingw,https://www.mingw-w64.org/downloads/#w64devkit hiredis、zstd使用mingw编译 cmake -G “MinGW Makefiles” cmakecache.txt手动修改去掉网络下载,…...

Apache POI使用

1.导入坐标 <!-- poi --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>${poi}</version></dependency><dependency><groupId>org.apache.poi</groupId><a…...

UNIQUE VISION Programming Contest 2023 Autumn(AtCoder Beginner Contest 323)

A - Weak Beats 链接 : A - Weak Beats 思路 : 模拟即可,如果在偶数位上出现了非0得元素&#xff0c;直接输出"No"后返回即可&#xff0c;循环顺利结束的话&#xff0c;就直接输出"Yes"; 代码 : #include<bits/stdc.h> #define IOS ios::sy…...

Docker 网络管理

Docker 网络实现原理 Docker使用Linux桥接&#xff0c;在宿主机虚拟一个Docker容器网桥(docker0)&#xff0c;Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址&#xff0c;称为Container-IP&#xff0c;同时Docker网桥是每个容器的默认网关。因为在同一宿主机…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...