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中,需要实现flutter与Javascript交互,在使用webview_flutter插件的时候,整理了一下webview与Javascript的交互JSBridge,具体可以查看 https:/…...

私有云盘:lamp部署nextcloud+高可用集群
目录 一、实验准备: 二、配置mariadb主从复制 三台主机下载mariadb 1)主的操作 2)从的操作 3)测试数据是否同步 三、配置nfs让web服务挂载 1、安装 2、配置nfs服务器 3、配置web服务的httpd 4、测试 四、web 服务器 配…...

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

聊聊分布式架构06——[NIO入门]简单的Netty NIO示例
目录 Java NIO和Netty NIO比较 Java NIO: Netty: Netty NIO中的主要模块 Transport(传输层) Buffer(缓冲区) Codec(编解码器) Handler(处理器) Even…...

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

解决Ubuntu18.04安装好搜狗输入法后无法打出中文的问题
首先下载安装 搜狗拼音输入法 ,下载选择: x86_64 在ubuntu中设置 fcitx 最后发现安装好了,图标有了 ,但是使用时不能输入中文,使用下面的命令解决: 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中显示图像效果: 2.算法运行软件版本 vivado2019.2 matlab2022a 3.部分核心程序 timescale 1ns / 1p…...

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

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

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

疯狂java 1.7垃圾回收机制
内存泄漏:如果一些分配出去的内存得不到及时回收,就会引起系统运行速度下降,甚至导致程序瘫痪 Java程序的内存分配和回收都是由JRE在后台自动进行的。JRE会负责回收哪些不再使用的内存,这种机制被称为垃圾回收(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服务端的接收确认(ACK)和消费者通过手动确认或者丢弃消费的消息。 通过配置 publisher-confirm-type: correlated 和publisher-returns: true开启生产者确认消息。 server:port: 8014spring:rabbitmq:username: …...

嵌入式系统开发【深入浅出】 GPIO 类设备的驱动程序
目录 GPIO管脚的输出功能相当于控制、输入相当于检测 使用GPIO基本流程 对于某一个管脚来说最多有几种功能? 拓展 【定时器与系统定时器】 决定定时长短的因素: 普通定时器 系统定时器 STM32F103RBT6的时钟源有哪五种 sysclk 的时钟频率由哪个时钟源提供基…...

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

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得元素,直接输出"No"后返回即可,循环顺利结束的话,就直接输出"Yes"; 代码 : #include<bits/stdc.h> #define IOS ios::sy…...

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

网络安全国家队-安防思考与实践
按照工信部“三同步”安全建设的统一要求,本项目的实施应具备符合等级保护要求的安全防护措施(主要为传输控制、防火墙隔离、入侵检测、安全审计等网络安全措施;操作系统安全、数据库安全、防病毒管理、安全审计等基础系统安全措施࿰…...

epoll 定时器
参考: Linux下使用epoll监听定时器-CSDN博客 但是这个用的是gettimeofday。 本人使用的是 #include <stdlib.h> #include<stdio.h> #include <sys/timerfd.h> #include <sys/epoll.h> #include <unistd.h> #include <sys/time.…...

BUUCTF Java逆向解密 1
Class文件是Java编译后的二进制字节码文件。 我这里使用的是jadx-gui,直接将class文件拖进去即可 package defpackage;import java.util.ArrayList; import java.util.Scanner;/* renamed from: Reverse reason: default package */ /* loaded from: Reverse.clas…...

BUUCTF [MRCTF2020]Ez_bypass1
这道题全程我都是用bp做的 拿到题目 我们查看页面源代码得到 代码审计 我们要用get传入id和gg两个参数,id和gg的值要求不能相等,但是id和gg的md5强比较必须相等 if(isset($_GET[gg])&&isset($_GET[id])) {$id$_GET[id];$gg$_GET[gg];if (md5($…...

深入理解强化学习——强化学习和有监督学习
分类目录:《深入理解强化学习》总目录 通过前文的介绍,我们现在应该已经对强化学习的基本数学概念有了一定的了解。这里我们回过头来再看看一般的有监督学习和强化学习的区别。以图片分类为例,有监督学习(Supervised Learning&…...

设计模式 - 结构型模式考点篇:装饰者模式(概念 | 案例实现 | 优缺点 | 使用场景)
目录 一、结构型模式 1.1、装饰者模式 1.1.1、概念 1.1.2、案例实现 1.1.3、优缺点 1.1.4、使用场景 一、结构型模式 1.1、装饰者模式 1.1.1、概念 装饰者模式就是指在不改变现有对象结构的情况下,动态的给该对象增加一些职责(增加额外功能&#…...

计算机竞赛 题目:基于深度学习的手势识别实现
文章目录 1 前言2 项目背景3 任务描述4 环境搭配5 项目实现5.1 准备数据5.2 构建网络5.3 开始训练5.4 模型评估 6 识别效果7 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的手势识别实现 该项目较为新颖,适合作为竞赛课题…...

手撕各种排序
> 作者简介:დ旧言~,目前大一,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:掌握每种排序的方法,理解每种排序利弊…...

视频号的链接在哪,视频号视频链接地址获取办法!
不少人问视频号的链接在哪里可以获取,本质的在腾讯微信中目前视频号的链接是无法获取的,但好事多磨今天就分享一个第三方的视频号视频链接地址获取办法,希望对你有所帮助! 1:在微信客户端中,我们可以通过搜…...

深度学习笔记之优化算法(六)RMSprop算法的简单认识
深度学习笔记之优化算法——RMSProp算法的简单认识 引言回顾:AdaGrad算法AdaGrad算法与动量法的优化方式区别AdaGrad算法的缺陷 RMProp算法关于AdaGrad问题的优化方式RMSProp的算法过程描述 RMSProp示例代码 引言 上一节对 AdaGrad \text{AdaGrad} AdaGrad算法进行…...