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

邵阳做网站哪个公司好接推广网站

邵阳做网站哪个公司好,接推广网站,做影视网站用的封面,企业邮箱账号在哪里看flutter开发实战-实现webview与H5中Javascript通信JSBridge 在开发中#xff0c;使用到webview#xff0c;flutter实现webview是使用原生的插件实现#xff0c;常用的有webview_flutter与flutter_inappwebview 这里使用的是webview_flutter#xff0c;在iOS上#xff0c;…flutter开发实战-实现webview与H5中Javascript通信JSBridge 在开发中使用到webviewflutter实现webview是使用原生的插件实现常用的有webview_flutter与flutter_inappwebview 这里使用的是webview_flutter在iOS上WebView小部件由WKWebView支持。在Android上WebView小部件由WebView支持。 这里使用的是webview_flutter的3.0.4版本不同版本代码变化还是挺大的。 一、引webview_flutter 在工程中pubspec.yaml引入webview_flutter # 浏览器webview_flutter: ^3.0.4webview_cookie_manager: ^2.0.6二、使用webview 2.1、webview webview的属性 const WebView({Key? key,this.onWebViewCreated,this.initialUrl,this.initialCookies const WebViewCookie[],this.javascriptMode JavascriptMode.disabled,this.javascriptChannels,this.navigationDelegate,this.gestureRecognizers,this.onPageStarted,this.onPageFinished,this.onProgress,this.onWebResourceError,this.debuggingEnabled false,this.gestureNavigationEnabled false,this.userAgent,this.zoomEnabled true,this.initialMediaPlaybackPolicy AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,this.allowsInlineMediaPlayback false,this.backgroundColor,})flutter webview和JS交互需要JavaScript开启。 flutter webview中的javascriptMode参数启用或禁用 JavaScript。默认情况下WebView的 JavaScript是禁用的所以要想启用的话可以使用JavascriptMode.unrestricted WebView(initialUrl: https://www.laileshuo.com,javascriptMode: JavascriptMode.unrestricted, )flutter webview提供WebViewController来获取webview信息以及控制webview的刷新、loadUrl、前进、后退等功能。 WebView(initialUrl: https://www.laileshuo.com,onWebViewCreated: (WebViewController webViewController) {_controller webViewController;}, );2.2、JavascriptChannel JavascriptChannel用于接收在web视图中运行的JavaScript代码发出的消息提供了name与onMessageReceived。 JavascriptChannel({required this.name,required this.onMessageReceived,})我们需要在Webview的javascriptChannels属性设置javascriptChannel! javascriptChannels: JavascriptChannel{_jsChannelManager.javascriptChannel!,},2.3、Cookie 在使用webview的cookie时候使用initialCookies设置cookie列表 这里我们定义了JSCookieConfig来设置需要设置的cookie // 处理注入到webview的cookie设置cookie通过webview_cookie_manager设置所需要的cookie列表 // Cookie不同应用对应不同的keyvalue为token class JSCookieConfig {JSCookieConfig() {eventListener();}// cookiefinal WebviewCookieManager cookieManager WebviewCookieManager();ListWebViewCookie initialCookies() {LoggerManager().debug(initialCookies ApiAuth().token:${ApiAuth.getToken()});ListWebViewCookie cookies [WebViewCookie(name: app_authorization,value: ApiAuth.getToken(),domain: .ifour.cn),WebViewCookie(name: token, value: ApiAuth.getToken(), domain: .ifour.cn),];return cookies;}Futurevoid setCookies() async {// final mainCookie Cookie(app_authorization, ApiAuth().token)..domain ifour.cn;// final h5_tokenCookie Cookie(token, ApiAuth().token)..domain ifour.cn;//// await cookieManager.setCookies([// mainCookie,// h5_tokenCookie// ]);await cookieManager.setCookies([Cookie(app_authorization, ApiAuth.getToken())..domain .ifour.cn..httpOnly false,Cookie(token, ApiAuth.getToken())..domain .ifour.cn..httpOnly false,]);}Futurevoid clear() async {await cookieManager.clearCookies();}void eventListener() {AppEventBus().on(kUserLoginChanged, this, (arg) {setCookies();});}// 注入cookie // String cookieJS // document.cookie app_authorization${ApiAuth().token};domain.ifour.cn;path/; // // _jsChannelManager.injectJavascript(cookieJS); }2.4、注入JS JSBridge实现webview上原生与h5的通信js可以调用nativenative也可以调用js实现通信。 其主要是通过拦截 URL 请求来达到 native 端和 webview 端相互通信的效果常用的是WebviewJavascriptBridge 这里我们使用代码将WebviewJavascriptBridge的JS代码注入到flutter webview中。 flutter使用的WebviewJavascriptBridge的代码 const String kWebviewJavascriptBridge 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 CUSTOM_PROTOCOL_SCHEME https;var QUEUE_HAS_MESSAGE __wvjb_queue_message__;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);// messagingIframe.src CUSTOM_PROTOCOL_SCHEME :// QUEUE_HAS_MESSAGE;// 通过JavaScriptChannel注入的全局对象window.JSAppSDK.postMessage(JSON.stringify(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);}// messagingIframe document.createElement(iframe);// messagingIframe.style.display none;// messagingIframe.src CUSTOM_PROTOCOL_SCHEME :// QUEUE_HAS_MESSAGE;// document.documentElement.appendChild(messagingIframe);registerHandler(_disableJavascriptAlertBoxSafetyTimeout, disableJavscriptAlertBoxSafetyTimeout);// setTimeout(_callWVJBCallbacks, 0);// function _callWVJBCallbacks() {// var callbacks window.WVJBCallbacks;// delete window.WVJBCallbacks;// for (var i0; icallbacks.length; i) {// callbacks[i](AppJSBridge);// }// }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); }preprocessorJS() ;setupWebViewJavascriptBridge与setupWebViewJavascriptBridge判断window.AppJSBridge是否存在通过监听AppJSBridgeReady来实现window.AppJSBridge初始化之后js中就可以使用window.AppJSBridge中的registerHandler、callHandler等方法了。 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);}// if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }// window.WVJBCallbacks [callback];// var WVJBIframe document.createElement(iframe);// WVJBIframe.style.display none;// WVJBIframe.src https://__bridge_loaded__;// document.documentElement.appendChild(WVJBIframe);// setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)}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)});} ;在webview的onWebViewCreated将kWebviewJsBridgeReady代码注入进行监听window.AppJSBridge是否可用。 注入的代码webController的runJavascript方法 _jsChannelManager中的代码 // 注入jsvoid injectJavascriptReady() async {await webController?.runJavascript(javascript:$kWebviewJsBridgeReady);}webview的onWebViewCreatedwebview创建后 onWebViewCreated: (controller) {LoggerManager().debug(onWebViewCreated);// 注入jsReady_jsChannelManager.injectJavascriptReady();},在webview的onPageFinished将kWebviewJavascriptBridge代码注入 onPageFinished: (String url) {// 网页加载完成LoggerManager().debug(onPageFinished url: $url);// 注入_jsChannelManager.injectBridgeJavascript();},2.5、实现JSChannelManager管理处理H5与flutter webview通信 JSChannelManager中使用JavascriptChannel来接收h5端的JS消息。 当收到H5消息的时候flutter根据callbackId回调给H5 实现的具体代码如下 const String kJSChannelName JSAppSDK;const String kOldProtocolScheme wvjbscheme; const String kNewProtocolScheme https; const String kQueueHasMessage __wvjb_queue_message__; const String kBridgeLoaded __bridge_loaded__;class JSChannelManager {WebViewController? webController;BuildContext? context;JavascriptChannel? javascriptChannel;// 存储的消息messageHandlerMapString, dynamic messageHandlers {};// 存储的回调callback, responseCallbackMapString, dynamic responseCallbacks {};// 开启的消息队列发送的消息均会存储到该队列中ListJSMessage? startupMessageQueue [];// 消息的标识int _uniqueId 0;JSChannelManager() {javascriptChannel JavascriptChannel(name: kJSChannelName,onMessageReceived: (JavascriptMessage message) {// 将JSON字符串转成MapLoggerManager().debug(onMessageReceived message:${message.message});flutterFlushMessageQueue();},);}void updateController(WebViewController controller, BuildContext context) {this.webController controller;this.context context;}JavascriptChannel getJSChannel() {return javascriptChannel!;}// 处理消息队列void flutterFlushMessageQueue() async {// 获取h5发送的列表// 处理H5存的消息队列发送的MessageQueueString? messageQueueString await webController?.runJavascriptReturningResult(webViewJavascriptFetchQueyCommand());LoggerManager().debug(flutterFlushMessageQueue:${messageQueueString});flushMessageQueue(messageQueueString);}// 处理来自H5的消息列表void flushMessageQueue(String? messageQueueString) {if (!(messageQueueString ! null messageQueueString.isNotEmpty)) {return;}LoggerManager().debug(flushMessageQueue messageQueueString:${messageQueueString});dynamic? aFromH5Messages jsonDecode(messageQueueString);LoggerManager().debug(flushMessageQueue 1111 aFromH5Messages:${aFromH5Messages}, type:${aFromH5Messages.runtimeType});if (aFromH5Messages ! null aFromH5Messages is String) {aFromH5Messages jsonDecode(aFromH5Messages);}LoggerManager().debug(flushMessageQueue 222 aFromH5Messages:${aFromH5Messages}, type:${aFromH5Messages.runtimeType});if (aFromH5Messages ! null aFromH5Messages is List) {for (dynamic aMsgJson in aFromH5Messages) {if (aMsgJson is MapString, 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);await webController?.runJavascript(javascriptCommand);}// 注入jsvoid injectJavascript(String javascript) async {await webController?.runJavascript(javascript);}// 注入jsvoid injectJavascriptReady() async {await webController?.runJavascript(javascript:$kWebviewJsBridgeReady);}// 注入jsvoid injectBridgeJavascript() async {await webController?.runJavascript(javascript:$kWebviewJavascriptBridge);LoggerManager().debug(injectJavascript);// 处理flutter发送的消息队列if (startupMessageQueue ! null startupMessageQueue!.isNotEmpty) {ListJSMessage 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);}// 判断是否可以注入urlbool isWebViewJavascriptBridgeURL(String url) {if (!isSchemeMatch(url)) {return false;}return isBridgeLoadedURL(url) || isQueueMessageURL(url);}bool isSchemeMatch(String url) {String lowerUrl url.toLowerCase();LoggerManager().debug(isSchemeMatch lowerUrl${lowerUrl});return (lowerUrl.startsWith(kNewProtocolScheme) ||lowerUrl.startsWith(kOldProtocolScheme));}bool isQueueMessageURL(String url) {String lowerUrl url.toLowerCase();LoggerManager().debug(isQueueMessageURL lowerUrl${lowerUrl});return (isSchemeMatch(url) (lowerUrl.contains(kQueueHasMessage)));}bool isBridgeLoadedURL(String url) {String lowerUrl url.toLowerCase();LoggerManager().debug(isBridgeLoadedURL lowerUrl${lowerUrl});return (isSchemeMatch(url) (lowerUrl.contains(kBridgeLoaded)));}// 注入js的commandString webViewJavascriptCheckCommand() {return typeof window.AppJSBridge \object\;;}String webViewJavascriptFetchQueyCommand() {return AppJSBridge._fetchQueue();;}String webViewJavascriptHandleMessageFromObjCCommand(String messageJSON) {return AppJSBridge._handleMessageFromObjC(${messageJSON});;}// 判断AppJSBridgeFutureString? checkJavascriptBridge() async {String? result await webController?.runJavascriptReturningResult(webViewJavascriptCheckCommand());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 handleName, JSBridgeHandler jsBridgeHandler) {if (handleName.isNotEmpty) {messageHandlers[handleName] jsBridgeHandler;}}// 移除注册的方法void removeHandler(String handleName) {if (handleName.isNotEmpty) {messageHandlers.remove(handleName);}}// 重置将responseCallbacks、startupMessageQueue重置void reset() {startupMessageQueue [];responseCallbacks {};_uniqueId 0;} }2.6、JSChannelRegisterappBridge调用的方法flutter注册的方法 JSChannelRegister实现处理flutter注册的方法提供相应的方法H5端的JS可以方便调用。 // appBridge调用的方法flutter注册的方法 class JSChannelRegister {late JSChannelManager _jsChannelManager;// 支付final ChannelPayPlatform _channelPayPlatform ChannelPayPlatform();// 打开app等final ChannelLauncher _channelLauncher ChannelLauncher();// 弹窗final ChannelDialog _channelDialog ChannelDialog();// 扫码或者识别二维码final ChannelQrScanner _channelQrScanner ChannelQrScanner();JSChannelRegister({required JSChannelManager jsChannelManager}) {_jsChannelManager jsChannelManager;}// 注册handlersvoid registerHandlers({JSChannelRegisterHandler? jsChannelRegisterHandler}) {// 设置标题_jsChannelManager.registerHandler(JSChannelRegisterMethod.setTitle,(data, responseCallback) {if (data ! null data is String) {String title data;if (jsChannelRegisterHandler ! null) {jsChannelRegisterHandler(JSChannelRegisterMethod.setTitle, title);}}});// 获取用户昵称_jsChannelManager.registerHandler(JSChannelRegisterMethod.getUsername,(data, responseCallback) {UserModel userModel Provider.ofUserModel(OneContext().context!, listen: false);String userNickName userModel.userNickName ?? ;if (responseCallback ! null) {responseCallback(userNickName);}});// 获取定位_jsChannelManager.registerHandler(JSChannelRegisterMethod.getLoc,(data, responseCallback) {// TODO 获取定位});// 获取App名称_jsChannelManager.registerHandler(JSChannelRegisterMethod.getAppName,(data, responseCallback) {PackageInfo.fromPlatform().then((packageInfo) {String appName ${packageInfo.appName};if (responseCallback ! null) {responseCallback(appName);}});});// 获取版本号_jsChannelManager.registerHandler(JSChannelRegisterMethod.getVersion,(data, responseCallback) {PackageInfo.fromPlatform().then((packageInfo) {String version ${packageInfo.buildNumber};String versionCode version.replaceAll(., );if (responseCallback ! null) {responseCallback(versionCode);}});});// 获取用户id_jsChannelManager.registerHandler(JSChannelRegisterMethod.getUserId,(data, responseCallback) {UserModel userModel Provider.ofUserModel(OneContext().context!, listen: false);String userId userModel.userId ?? ;if (responseCallback ! null) {responseCallback(userId);}});// 获取用户登录认证token_jsChannelManager.registerHandler(JSChannelRegisterMethod.getAuthorization,(data, responseCallback) {UserModel userModel Provider.ofUserModel(OneContext().context!, listen: false);String token userModel.token ?? ;if (responseCallback ! null) {responseCallback(token);}});// 调用支付微信支付/支付宝支付原生_jsChannelManager.registerHandler(JSChannelRegisterMethod.setPayPlatform,(data, responseCallback) {_channelPayPlatform.openUniPay(data, responseCallback);});// 打开扫一扫_jsChannelManager.registerHandler(JSChannelRegisterMethod.openScan,(data, responseCallback) {// 打开扫一扫界面_channelQrScanner.openScanner(JSChannelRegisterMethod.openScan, data, responseCallback);});// 打开扫一扫_jsChannelManager.registerHandler(JSChannelRegisterMethod.scanQrCode,(data, responseCallback) {// 打开扫一扫界面_channelQrScanner.openScanner(JSChannelRegisterMethod.scanQrCode, data, responseCallback);});// 打系统电话_jsChannelManager.registerHandler(JSChannelRegisterMethod.callTelPhone,(data, responseCallback) {_channelLauncher.openLauncher(JSChannelRegisterMethod.callTelPhone, data, responseCallback);});// 发送短信_jsChannelManager.registerHandler(JSChannelRegisterMethod.sendSms,(data, responseCallback) {_channelLauncher.openLauncher(JSChannelRegisterMethod.sendSms, data, responseCallback);});// 对话框 showDialog_jsChannelManager.registerHandler(JSChannelRegisterMethod.showDialog,(data, responseCallback) {_channelDialog.openShowDialog(data, responseCallback);});// 底部选择框_jsChannelManager.registerHandler(JSChannelRegisterMethod.showCheckBox,(data, responseCallback) {_channelDialog.openShowSheetBox(data, responseCallback);});// 保存图片到相册_jsChannelManager.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);}});}});// 识别二维码_jsChannelManager.registerHandler(JSChannelRegisterMethod.detectorQRCode,(data, responseCallback) {// 识别图片中的二维码_channelQrScanner.openScanner(JSChannelRegisterMethod.detectorQRCode, data, responseCallback);});// 打开App_jsChannelManager.registerHandler(JSChannelRegisterMethod.openApp,(data, responseCallback) {_channelLauncher.openLauncher(JSChannelRegisterMethod.openApp, data, responseCallback);});// log_jsChannelManager.registerHandler(JSChannelRegisterMethod.log,(data, responseCallback) {MapString, 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});}});}// 处理是否跳转true可跳转false不可跳转bool navigationDecision(NavigationRequest request) {///在页面跳转之前调用 isForMainFrame为false,页面不跳转.导致网页内很多链接点击没效果String url Uri.decodeComponent(request.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;} }使用JSChannelRegister处理相应的callback overridevoid initState() {// TODO: implement initStatesuper.initState();_isDisposed false;_jsChannelRegister JSChannelRegister(jsChannelManager: _jsChannelManager);_jsChannelRegister.registerHandlers(jsChannelRegisterHandler: (handlerName, data) {if (JSChannelRegisterMethod.setTitle handlerName) {setWebPageTitle(data);}});}2.7、JSMessageH5和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(MapString, dynamic json) {callbackId json[callbackId];data json[data];handlerName json[handlerName];responseId json[responseId];responseData json[responseData];}MapString, dynamic toJson() {final MapString, dynamic data new MapString, 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;} }三、H5前端 我这里使用的是本地Html文件在JS中调用window.AppJSBridge中的方法如callHandler、registerHandler。 Html示例代码 !DOCTYPE html htmlhead meta nameviewport contentuser-scalableno, widthdevice-width, initial-scale1.0, maximum-scale1.0 / meta http-equivContent-Type contenttext/html; charsetutf-8 /style typetext/cssbody{background: #f5faff;}.button{width: 100%;line-height: 38px;text-align: center;font-weight: bold;color: #fff;text-shadow:1px 1px 1px #333;margin:0 auto;}.button:nth-child(6n){margin-right: 0;}.button.gray{color: #8c96a0;text-shadow:1px 1px 1px #fff;border:1px solid #dce1e6;box-shadow: 0 1px 2px #fff inset,0 -1px 0 #a8abae inset;background: -webkit-linear-gradient(top,#f2f3f7,#e4e8ec);background: -moz-linear-gradient(top,#f2f3f7,#e4e8ec);background: linear-gradient(top,#f2f3f7,#e4e8ec);}/styletitleJSBridge调用示例常用方法调用/title/head bodybutton typebutton classbutton gray idgetUsernamegetUsername/buttonbutton typebutton classbutton gray idgetLocgetLoc/buttonbutton typebutton classbutton gray idgetVersiongetVersion/buttonbutton typebutton classbutton gray idscanQrCodescanQrCode/buttonbutton typebutton classbutton gray idsetMenuItemssetMenuItems/buttonbutton typebutton classbutton gray idcallTelPhonecallTelPhone/buttonbutton typebutton classbutton gray idwebImagePreviewwebImagePreview/buttonbutton typebutton classbutton gray idshowCheckBoxshowCheckBox/buttonbutton typebutton classbutton gray idshowDialogshowDialog/buttonbutton typebutton classbutton gray idsaveImagesaveImage/buttonbutton typebutton classbutton gray idopenApp打开其他App/buttonscriptvar imgURL http://tupian.qqjay.com/tou3/2016/0726/fc4fe6f04843172bd6dbfeb5b6fe0686.jpg;var title 分享券var desc 分享券描述内容var url http://www.laileshuo.comvar wxSharedObject {thumb: imgURL,title: title,desc: desc,url: url};var appSharedObject {thumb: imgURL,title: title,desc: desc,url: url};var getUsernamedocument.getElementById(getUsername);getUsername.addEventListener(click,function(){AppJSBridge.callHandler(getUsername, , function(response) {window.alert(response)});});var getLocdocument.getElementById(getLoc);getLoc.addEventListener(click,function(){AppJSBridge.callHandler(getLoc, , function(response) {window.alert(response)});});var getVersiondocument.getElementById(getVersion);getVersion.addEventListener(click,function(){AppJSBridge.callHandler(getVersion, , function(response) {window.alert(response)});});var scanQrCodedocument.getElementById(scanQrCode);scanQrCode.addEventListener(click,function(){AppJSBridge.callHandler(scanQrCode, , function(response) {window.alert(response)});});var setMenuItemsdocument.getElementById(setMenuItems);setMenuItems.addEventListener(click,function(){AppJSBridge.callHandler(setMenuItems, wxinFreind,wxinTime,weibo,refresh, function(response) {});});var callTelPhonedocument.getElementById(callTelPhone);var telPhone 10086,10086;callTelPhone.addEventListener(click,function(){AppJSBridge.callHandler(callTelPhone, telPhone, function(response) {// log(JS got response, response)});});var webImagePreviewdocument.getElementById(webImagePreview);var previewData {imgs : [ //图片列表数组http://7sbytg.com1.z0.glb.clouddn.com/yz2.png,http://7sbytg.com1.z0.glb.clouddn.com/yz2.png],index : 0 //进入预览时显示第几个图片};webImagePreview.addEventListener(click,function(){AppJSBridge.callHandler(webImagePreview, JSON.stringify(previewData), function(response) {});});var showCheckBoxdocument.getElementById(showCheckBox);var bottomBox {optionList : [删除, 兑换, 其他] //选项列表选项列表对应自己的index};showCheckBox.addEventListener(click,function(){AppJSBridge.callHandler(showCheckBox, JSON.stringify(bottomBox), function(response) {window.alert(response)});});var showDialogdocument.getElementById(showDialog);var dialog {title : 标题, // Dialog标题message : 对话框内容, // Dialog内容可选ok : 确定, // 确认按钮的文字可选不填时不显示该按钮cancel : 取消 // 取消按钮的文字可选不填时不显示该按钮};showDialog.addEventListener(click,function(){AppJSBridge.callHandler(showDialog, JSON.stringify(dialog), function(response) {// log(JS got response, response)});});var saveImagedocument.getElementById(saveImage);saveImage.addEventListener(click,function(){AppJSBridge.callHandler(saveImage, https://c-ssl.duitang.com/uploads/item/201611/12/20161112230928_vJEQy.jpeg, function(response) {});});var openAppdocument.getElementById(openApp);openApp.addEventListener(click,function(){AppJSBridge.callHandler(openApp, weixin, function(response) {});});if (window.AppJSBridge) {var dialog {title : 标题, // Dialog标题message : 对话框内容, // Dialog内容可选ok : 确定, // 确认按钮的文字可选不填时不显示该按钮cancel : 取消 // 取消按钮的文字可选不填时不显示该按钮};AppJSBridge.callHandler(showDialog, JSON.stringify(dialog), function(response) {// log(JS got response, response)});}document.addEventListener(AppJSBridgeReady, function() {AppJSBridge.registerHandler(JSAPPHandler, function(data, responseCallback) {var responseData { Javascript Says:Right back atcha! }responseCallback(responseData)});var dialog {title : 标题, // Dialog标题message : 对话框内容, // Dialog内容可选ok : 确定, // 确认按钮的文字可选不填时不显示该按钮cancel : 取消 // 取消按钮的文字可选不填时不显示该按钮};AppJSBridge.callHandler(showDialog, JSON.stringify(dialog), function(response) {// log(JS got response, response)});}, false);//WKWebView 可用document.addEventListener(visibilitychange, () {if (document.hidden) {// 页面被挂起window.alert(document.visibilityState)} else {// 页面呼出window.alert(document.visibilityState)}})/script/body /html四、flutter的webView_page页面打开对应的Html页面 这里使用的JSChannelManager、JSCookieConfig、JSChannelRegister等flutter WebViewPage class WebViewPage extends StatefulWidget {const WebViewPage({Key? key,this.arguments,}) : super(key: key);final Object? arguments;overrideStateWebViewPage createState() _WebViewPageState(); }class _WebViewPageState extends StateWebViewPage {String title ;String? url;// WebViewControllerWebViewController? _webViewController;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(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: () {webViewGoBack();},icon: Icon(Icons.arrow_back_ios,color: Colors.white,size: 24.0,),),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,),IconButton(padding: EdgeInsets.all(0.0),onPressed: () {webViewReload();},icon: Icon(Icons.refresh_outlined,color: Colors.white,size: 28.0,),),],),),body: Stack(children: [WebViewSkeleton(url: url ?? ,onWebResourceError: (WebResourceError error) {if (mounted) {// TODO onWebResourceError}},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 ?? ;});}},onWebViewCreated: (WebViewController controller) {_webViewController controller;},),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();}void webViewGoBack() {_webViewController?.canGoBack().then((res) {// 是否能返回上一级LoggerManager().debug(controller.canGoBack res: $res);if (true res) {_webViewController?.goBack();} else {navigatorBack();}});}void webViewReload() {_webViewController?.reload();} }WebViewSkeleton class WebViewSkeleton extends StatefulWidget {const WebViewSkeleton({Key? key,required this.url,required this.onWebProgress,required this.onWebResourceError,required this.onLoadFinished,this.onWebTitleLoaded,required this.onWebViewCreated,}) : super(key: key);final String url;final Function(int progress) onWebProgress;final Function(WebResourceError error) onWebResourceError;final Function(String? url) onLoadFinished;final Function(String? webTitle)? onWebTitleLoaded;final Function(WebViewController controller) onWebViewCreated;static GlobalKey_WebViewSkeletonState getGlobalKey() GlobalKey();overrideStateWebViewSkeleton createState() _WebViewSkeletonState(); }class _WebViewSkeletonState extends StateWebViewSkeleton {// WebViewControllerWebViewController? _webController;// JS与Flutter调用的message Queuefinal JSChannelManager _jsChannelManager JSChannelManager();// cookiefinal JSCookieConfig _jsCookieConfig JSCookieConfig();// flutter注册供H5调用的方法late JSChannelRegister _jsChannelRegister;// 尝试3次每次间隔2秒int _loadTitleTimes 0;bool _isDisposed false;overridevoid initState() {// TODO: implement initStatesuper.initState();_isDisposed false;_jsChannelRegister JSChannelRegister(jsChannelManager: _jsChannelManager);_jsChannelRegister.registerHandlers(jsChannelRegisterHandler: (handlerName, data) {if (JSChannelRegisterMethod.setTitle handlerName) {setWebPageTitle(data);}});}overridevoid dispose() {// TODO: implement dispose_isDisposed true;_jsChannelManager.reset();_webController?.clearCache();// _jsCookieConfig.clear();super.dispose();}// flutter调用H5方法void callJSMethod() {_jsChannelManager.callHandler(JSAPPHandler, data: {id: a18c9fe0d},responseCallback: (dynamic responseData) {LoggerManager().debug(callJSMethod responseData:${responseData});FlutterLoadingHud.showToast(message: jsonEncode(responseData));});}void webPageLoadedStart() {_loadTitleTimes 0;}Futurevoid getWebPageTitle({required String url}) async {if (_isDisposed) {return;}String? title await _webController?.getTitle();LoggerManager().debug(getWebPageTitle${title});if (title ! null title.isNotEmpty) {LoggerManager().debug(webTitle a:${title});setWebPageTitle(title);} else {try {String? result await _webController?.runJavascriptReturningResult(window.document.title);LoggerManager().debug(webTitle document.url:${result});if (result ! null result.isNotEmpty) {setWebPageTitle(result);} else {result await _webController?.runJavascriptReturningResult(window.document.getElementsByTagName(title)[0]);LoggerManager().debug(webTitle document.getElementsByTagName:${result});setWebPageTitle(result);}} catch (e) {print(getWebPageTitle${e.toString()});// 最多尝试三次if (_loadTitleTimes 3) {Future.delayed(Duration(seconds: 2), () {_loadTitleTimes;getWebPageTitle(url: url);});}}}}// 设置页面标题void setWebPageTitle(data) {if (widget.onWebTitleLoaded ! null) {widget.onWebTitleLoaded!(data);}}// 返回void goBack() {_webController?.canGoBack().then((res) {// 是否能返回上一级LoggerManager().debug(controller.canGoBack res: $res);if (true res) {_webController?.goBack();}});}// 刷新void reload() {_webController?.reload();}overrideWidget build(BuildContext context) {return buildWebView(context);}Widget buildWebView(BuildContext context) {UserModel userModel Provider.ofUserModel(context, listen: false);LoggerManager().debug(ApiAuth().token${ApiAuth.getToken()});return WebView(debuggingEnabled: true,initialUrl: widget.url,javascriptMode: JavascriptMode.unrestricted,userAgent: app-yjxdh-webview,initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,allowsInlineMediaPlayback: true,initialCookies: _jsCookieConfig.initialCookies(),onWebViewCreated: (controller) {LoggerManager().debug(onWebViewCreated);_jsCookieConfig.setCookies();// controller.loadUrl(url)此时也可以初始化一个urlcontroller.canGoBack().then((res) {// 是否能返回上一级LoggerManager().debug(controller.canGoBack res: $res);});controller.currentUrl().then((url) {// 返回当前urlLoggerManager().debug(controller.currentUrl url: $url);});controller.canGoForward().then((res) {//是否能前进LoggerManager().debug(controller.canGoForward res: $res);});_webController controller;_jsChannelManager.updateController(controller, context);String filePre file://;if (widget.url.startsWith(filePre)) {String html widget.url.substring(filePre.length);DefaultAssetBundle.of(context).loadString(assets/htmls/${html}).then((value) _webController?.loadHtmlString(value));} else {if (widget.url.startsWith(http://) ||widget.url.startsWith(https://)) {_webController?.loadUrl(widget.url, headers: {Referer: widget.url,});}}// 注入jsReady_jsChannelManager.injectJavascriptReady();widget.onWebViewCreated(controller);},onProgress: (int progress) {widget.onWebProgress(progress);},javascriptChannels: JavascriptChannel{_jsChannelManager.javascriptChannel!,},navigationDelegate: (NavigationRequest request) {bool canNavigate _jsChannelRegister.navigationDecision(request);// 允许路由替换return canNavigate? NavigationDecision.navigate: NavigationDecision.prevent;},onPageStarted: (String url) {// 网页开始加载webPageLoadedStart();LoggerManager().debug(onPageStarted url: $url);},onPageFinished: (String url) {// 网页加载完成LoggerManager().debug(onPageFinished url: $url);// 注入_jsChannelManager.injectBridgeJavascript();_jsChannelManager.checkJavascriptBridge();// 加载完成widget.onLoadFinished(url);// 获取网页的标题getWebPageTitle(url: url);},gestureNavigationEnabled: true,backgroundColor: ColorUtil.hexColor(0xf7f7f7),onWebResourceError: (WebResourceError error) {/// errorLoggerManager().debug(onWebResourceError:${error});widget.onWebResourceError(error);},);}Widget buildButtonRow(BuildContext context) {return Row(mainAxisAlignment: MainAxisAlignment.end,crossAxisAlignment: CrossAxisAlignment.center,children: [buildButton(context),SizedBox(width: 10.0,),buildRefreshButton(context),],);}// 展开的按钮Widget buildButton(BuildContext context) {return Container(decoration: BoxDecoration(color: Colors.white,border: Border.all(color: Colors.black26,width: 1.0,style: BorderStyle.solid,),borderRadius: BorderRadius.all(Radius.circular(8.0),),),child: TextButton(onPressed: () {callJSMethod();},child: Text(调用JS方法菜单,style: TextStyle(fontSize: 12,color: Colors.black,),),),);}// 刷新按钮Widget buildRefreshButton(BuildContext context) {return Container(decoration: BoxDecoration(color: Colors.white,border: Border.all(color: Colors.black26,width: 1.0,style: BorderStyle.solid,),borderRadius: BorderRadius.all(Radius.circular(8.0),),),child: TextButton(onPressed: () {reload();},child: Text(刷新WebView,style: TextStyle(fontSize: 12,color: Colors.black,),),),);} }六、运行效果图 五、小结 flutter开发实战-webview_flutter结合javascriptbridge实现flutter与html交互通过使用flutter webview通过javascriptBridge来进行交互、交互用到了JavascriptChannel、cookie等。代码是好久之前写的现在文档整理的有点乱代码中基本上都有注释。希望有对你有用的点。 学习记录每天不停进步。
http://www.zqtcl.cn/news/220614/

相关文章:

  • 网站服务器端口设置北京专业网络直播制作
  • 可以免费做演播的听书网站南京企业自助建站
  • 软件下载类型网站怎么做长沙官网优化技术
  • 药品网站订单源码外贸网站建设服务器
  • 深圳网站制作07551免费开发网站
  • 如何直接用jsp做网站不写servletwordpress模板 单栏
  • 长沙网站建设哪个公司好设计公司网站 唐山
  • 原创小说手机网站制作需要多少钱郴州seo外包
  • 深圳市大鹏建设局网站网站关键词没排名怎么办
  • 水果商城网站制作多少钱c#如何做公司网站
  • 国内做进口的电商网站网站建设的经验做法
  • 蚂蚁搬家公司官方网站免费网站软件制作
  • 搭建网站要用到的工具外链代发免费
  • 肥城网站建设流程oem中国代加工网
  • 到底建手机网站还是电脑网站网站视频怎么做
  • 小区网站建设前端手机网站
  • 做一个网站价格WordPress好看的404
  • 查看注册过的网站在线网站软件免费下载
  • 门户网站建设公司价位域名出售网站
  • 亿级流量网站架构自己制作一个网站
  • 企业网站seo成功案例天津网站建设制作品牌公司
  • 衡水做网站电话郏县建设局网站
  • 美工做网站尺寸多少钱怎么做网站免费的
  • 会计信息系统网站建设流程图手机网站图片宽度
  • 已备案网站增加域名wordpress 百度熊掌号
  • 网站建设维护课件ppt百度搜索一下百度
  • 重庆企业网站开发方案wordpress菜单插件
  • 江苏网站seo设计什么学习网站建设
  • 青海网站建设系统电商出口营销要多少钱
  • 上海的网站设计公司百度上做优化