国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge

這篇具有很好參考價值的文章主要介紹了flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge

在使用webview中,需要實現flutter與Javascript交互,在使用webview_flutter插件的時候,整理了一下webview與Javascript的交互JSBridge,具體可以查看

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

這里使用inappwebview插件來實現flutter與Javascript的交互JSBridge。

一、什么是JSBridge

JSBridge是一種實現webview與原生端的相互調用的能力。

在比較流行的JSBridge中,主要是通過攔截URL請求來達到 native 端和 webview 端相互通信的效果的。如WebviewJavascriptBridge。

那在inappwebview中有實現javascript交互的方式。在inappwebview中,可以使用JavaScript Handlers,來實現flutter端與javascript的交互??梢圆榭?/p>

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

flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge,移動開發(fā),flutter開發(fā)實戰(zhàn),flutter,flutter,javascript,交互,inappwebview,HTML5,JSBridge,webview

二、修改JSBridge的JS端實現

在WebviewJavascriptBridge中,代碼中使用iframe中,攔截url來達到webview與原生交互。那在inappwebview,我們可以直接嵌套使用JavaScript Handlers來實現交互。

我們定義WebviewJSBridgeReady

const String kWebviewJsBridgeReady = '''
    window.onerror = function(err) {
        log('window.onerror: ' + err)
    }

    function setupWebViewJavascriptBridge(callback) {
        if (window.AppJSBridge) {
            return callback(AppJSBridge);
        } else {
            document.addEventListener('AppJSBridgeReady', function() {
                callback(AppJSBridge);
            },false);
        }
    }

    setupWebViewJavascriptBridge(function(bridge) {
        bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
            var responseData = { 'Javascript Says':'Right back atcha!' }
            responseCallback(responseData)
        });

        bridge.registerHandler('JSHandler', function(data, responseCallback) {
            var responseData = { 'Javascript Says':'Right back atcha!' }
            responseCallback(responseData)
        });
    }
''';


修改后的WebviewJsBridge,其中定義了sendMessageQueue、messageHandlers等。其中定義了一個window.AppJSBridge,創(chuàng)建了事件document.createEvent(‘Event’),初始化event.initEvent(‘AppJSBridgeReady’, true, true); 觸發(fā)對象dispatch觸發(fā)對象可以是任何元素或其他事件目標document.dispatchEvent(event);

const String kInAppWebViewJavascriptBridge = '''
function preprocessorJS() {
    if (window.AppJSBridge) {
		return;
	}

	if (!window.onerror) {
		window.onerror = function(msg, url, line) {
			console.log("AppJSBridge: ERROR:" + msg + "@" + url + ":" + line);
		}
	}

	// var messagingIframe;
	var sendMessageQueue = [];
	var messageHandlers = {};
	
	var responseCallbacks = {};
	var uniqueId = 1;
	var dispatchMessagesWithTimeoutSafety = true;

	function registerHandler(handlerName, handler) {
		messageHandlers[handlerName] = handler;
	}
	
	function callHandler(handlerName, data, responseCallback) {
		if (arguments.length == 2 && typeof data == 'function') {
			responseCallback = data;
			data = null;
		}
		_doSend({ handlerName:handlerName, data:data }, responseCallback);
	}
    
    function call(handlerName, data, responseCallback) {
        if (arguments.length == 2 && typeof data == 'function') {
            responseCallback = data;
            data = null;
        }
        _doSend({ handlerName:handlerName, data:data }, responseCallback);
    }
    
	function disableJavscriptAlertBoxSafetyTimeout() {
		dispatchMessagesWithTimeoutSafety = false;
	}
	
	function _doSend(message, responseCallback) {
		if (responseCallback) {
			var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
			responseCallbacks[callbackId] = responseCallback;
			message['callbackId'] = callbackId;
		}
		sendMessageQueue.push(message);
		// 通過使用inappwebview的callHandler
		window.flutter_inappwebview.callHandler(message['handlerName'], message);
	}

	function _fetchQueue() {
		var messageQueueString = JSON.stringify(sendMessageQueue);
		sendMessageQueue = [];
		return messageQueueString;
	}

	function _dispatchMessageFromObjC(messageJSON) {
		if (dispatchMessagesWithTimeoutSafety) {
			setTimeout(_doDispatchMessageFromObjC);
		} else {
			 _doDispatchMessageFromObjC();
		}
		
		// 打印log
		_consoleLog("AppJSBridge: messageJSON:" + messageJSON);
		
		function _doDispatchMessageFromObjC() {
			var message = JSON.parse(messageJSON);
			var messageHandler;
			var responseCallback;
			if (message.responseId) {
				responseCallback = responseCallbacks[message.responseId];
				if (!responseCallback) {
					return;
				}
				responseCallback(message.responseData);
				delete responseCallbacks[message.responseId];
			} else {
				if (message.callbackId) {
					var callbackResponseId = message.callbackId;
					responseCallback = function(responseData) {
						_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
					};
				}
				
				var handler = messageHandlers[message.handlerName];
				if (!handler) {
					_consoleLog("AppJSBridge: WARNING: no handler for message from ObjC:", message);
				} else {
					handler(message.data, responseCallback);
				}
			}
		}
	}
	
	function _handleMessageFromObjC(messageJSON) {
        _dispatchMessageFromObjC(messageJSON);
	}


	registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
	
    window.AppJSBridge = {
      registerHandler: registerHandler,
      callHandler: callHandler,
          call: call,
      disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
      _fetchQueue: _fetchQueue,
      _handleMessageFromObjC: _handleMessageFromObjC,
      _consoleLog: _consoleLog,
    };
    
    	// 打印log
	  function _consoleLog(message) {
	    // 顯示來自flutter的回調
	    var logJSON = { 'message':message, 'logType':1 }
		  callHandler("log", JSON.stringify(logJSON));
	  }
    
    window.WeixinJSBridge = window.AppJSBridge;
    
    // 創(chuàng)建事件
    var event = document.createEvent('Event');
    
    // 定義事件名為'build'.
    event.initEvent('AppJSBridgeReady', true, true);

    event.bridge = window.AppJSBridge;
    
    // 觸發(fā)對象可以是任何元素或其他事件目標
    document.dispatchEvent(event);
}

function preprocessorReadyJs() {
  if (!window.flutter_inappwebview.callHandler) {
      window.flutter_inappwebview.callHandler = function () {
          var _callHandlerID = setTimeout(function () { });
          window.flutter_inappwebview._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));
          return new Promise(function (resolve, reject) {
              window.flutter_inappwebview[_callHandlerID] = resolve;
          });
      };
  }
  
  preprocessorJS();
}

preprocessorReadyJs()

''';


三、在flutter端使用InAppWebViewController實現調用方法的InAppWebJSHandlerManager

在flutter端使用InAppWebViewController實現調用方法,使用InAppWebViewController來調用evaluateJavascript來調用js的方法。我們可以在flutter端實現調用flutter_inappwebview插件的JavaScript Handlers。這里我定義了消息隊列,定義了消息的JSMessage,與responseCallbacks回調。

// flutter_inappwebview插件的JavaScript Handlers
class InAppWebJSHandlerManager {
// flutter_inappwebview插件
  InAppWebViewController? inAppWebViewController;

  BuildContext? context;

  // 存儲的消息messageHandler
  Map<String, dynamic> messageHandlers = {};

  // 存儲的回調callback, responseCallback
  Map<String, dynamic> responseCallbacks = {};

  // 開啟的消息隊列,發(fā)送的消息均會存儲到該隊列中
  List<JSMessage>? startupMessageQueue = [];

  // 消息的標識
  int _uniqueId = 0;

  JSChannelManager() {
    // TODO: implement JSChannelManager
  }

  void updateController({
    required BuildContext context,
    InAppWebViewController? inAppWebViewController,
  }) {
    this.context = context;
    this.inAppWebViewController = inAppWebViewController;
  }

  // 處理消息隊列
  void flutterFlushMessageQueue() async {
    // 獲取h5發(fā)送的列表
    // 處理H5存的消息隊列發(fā)送的MessageQueue
    String? messageQueueString;
    if (inAppWebViewController != null) {
      messageQueueString = await inAppWebViewController
          ?.evaluateJavascript(source: webViewJavascriptFetchQueyCommand());
    }

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

  // 處理來自H5的消息列表
  void flushMessageQueue(String? messageQueueString) {
    if (!(messageQueueString != null && messageQueueString.isNotEmpty)) {
      return;
    }

    dynamic? aFromH5Messages = jsonDecode(messageQueueString);
    if (aFromH5Messages != null && aFromH5Messages is List) {
      for (dynamic aMsgJson in aFromH5Messages) {
        if (aMsgJson is Map<String, dynamic>) {
          JSMessage jsMessage = JSMessage.fromJson(aMsgJson);
          LoggerManager().debug(
              "flushMessageQueue aFromH5Messages aMsgJson:${aMsgJson} jsMessage:${jsMessage}");

          // 從H5獲取或者接收到的消息,如果responseId不為空,則為flutter調用H5方法,H5給flutter的回調
          if (jsMessage.responseId != null &&
              jsMessage.responseId!.isNotEmpty) {
            // 如果responseId不為空,則為flutter調用H5方法,H5給flutter的回調
            ResponseCallback? responseCallback =
            responseCallbacks[jsMessage.responseId];
            if (responseCallback != null) {
              // 處理H5返回給flutter的回調
              responseCallback(jsMessage.responseData);
            }
          } else {
            ResponseCallback? responseCallback;
            // 如果responseId為空時候,則是來自H5發(fā)送的flutter的消息
            // 獲取H5傳過來的標識callbackId
            String? callbackId = jsMessage.callbackId;
            if (callbackId != null && callbackId.isNotEmpty) {
              // 接收到來自H5的消息
              JSMessage aMessage = JSMessage();
              aMessage.copy(aNewMessage: aMessage, aOldMessage: jsMessage);

              responseCallback = (dynamic responseData) {
                // flutter回調給H5
                // 將H5傳過來的callbackId作為responseId回調傳遞給H5
                aMessage.responseId = callbackId;
                aMessage.responseData = responseData;
                _queueMessage(aMessage);
              };
            } else {
              responseCallback = (dynamic responseData) {
                // callbackId為空,不做任何處理
              };
            }

            // 從flutter已經注冊Register方法中找出對應的方法
            JSBridgeHandler? jsBridgeHandler =
            messageHandlers[jsMessage.handlerName];
            if (jsBridgeHandler != null) {
              // 在flutter該handlerName的方法已經注冊register
              jsBridgeHandler(jsMessage.data, responseCallback);
            } else {
              // 在flutter該handlerName沒有注冊,則不做任何處理
            }
          }
        }
      }
    }
  }

  // 處理從H5收到的消息
  void _dispatchMessage(JSMessage message) async {
    String messageJSON = jsonEncode(message.toJson());

    messageJSON = messageJSON.replaceAll("\\", "\\\\");
    messageJSON = messageJSON.replaceAll("\"", "\\\"");
    messageJSON = messageJSON.replaceAll("\'", "\\\'");
    messageJSON = messageJSON.replaceAll("\n", "\\n");
    messageJSON = messageJSON.replaceAll("\r", "\\r");
    messageJSON = messageJSON.replaceAll("\f", "\\f");
    messageJSON = messageJSON.replaceAll("\u2028", "\\u2028");
    messageJSON = messageJSON.replaceAll("\u2029", "\\u2029");

    String javascriptCommand =
    webViewJavascriptHandleMessageFromObjCCommand(messageJSON);

    if (inAppWebViewController != null) {
      await inAppWebViewController?.evaluateJavascript(source: javascriptCommand);
    }
  }

  // 注入js
  void injectJavascript(String javascript) async {
    if (inAppWebViewController != null) {
      await inAppWebViewController?.evaluateJavascript(source: javascript);
    }
  }

  // 注入js
  void injectJavascriptReady() async {
    if (inAppWebViewController != null) {
      await inAppWebViewController?.evaluateJavascript(source: '$kWebviewJsBridgeReady');
    }
  }

  // 注入js
  void injectBridgeJavascript() async {
    if (inAppWebViewController != null) {
      await inAppWebViewController?.evaluateJavascript(source: '$kInAppWebViewJavascriptBridge');
    }

    LoggerManager().debug("injectJavascript");

    // 處理flutter發(fā)送的消息隊列
    if (startupMessageQueue != null && startupMessageQueue!.isNotEmpty) {
      List<JSMessage> tmpList = startupMessageQueue!;
      startupMessageQueue = null;
      for (JSMessage message in tmpList) {
        _dispatchMessage(message);
      }
    }
  }

  // 向H5發(fā)送消息
  void _sendData(String handleName,
      {dynamic? data, ResponseCallback? responseCallback}) {
    String callbackId = "flutter_cb_${++_uniqueId}";

    JSMessage jsMessage = JSMessage();
    jsMessage.callbackId = callbackId;
    jsMessage.handlerName = handleName;
    jsMessage.data = data;

    // 將callbackId存儲到responseCallbacks中,callbackId會被H5通過responseId返回
    if (responseCallback != null) {
      responseCallbacks[callbackId] = responseCallback;
    }

    _queueMessage(jsMessage);
  }

  // 將發(fā)送給H5的消息存到startupMessageQueue中
  void _queueMessage(JSMessage jsMessage) {
    if (startupMessageQueue != null) {
      startupMessageQueue!.add(jsMessage);
    }

    _dispatchMessage(jsMessage);
  }

  // 注入js的command
  String webViewJavascriptCheckCommand() {
    return "typeof window.AppJSBridge == \'object\';";
  }

  String webViewJavascriptFetchQueyCommand() {
    return "AppJSBridge._fetchQueue();";
  }

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

  // 判斷AppJSBridge
  Future<String?> checkJavascriptBridge() async {
    String? result;
    if (inAppWebViewController != null) {
      bool jsBridge = await inAppWebViewController
          ?.evaluateJavascript(source: webViewJavascriptCheckCommand());
      result = (jsBridge?"true":"false");
    }
    LoggerManager().debug("checkJavascriptBridge result:${result}");
    return result;
  }

  /// flutter開放出去的方法,flutter調用H5方法統(tǒng)一使用該callHandler
  /// callHandler
  void callHandler(String handleName,
      {dynamic? data, ResponseCallback? responseCallback}) {
    if (handleName.isNotEmpty) {
      _sendData(handleName, data: data, responseCallback: responseCallback);
    }
  }

  /// flutter注冊方法
  /// flutter注冊方法,提供給H5調用
  void registerHandler(String handlerName, JSBridgeHandler jsBridgeHandler) {
    if (handlerName.isNotEmpty) {
      messageHandlers[handlerName] = jsBridgeHandler;
    }
  }

  void addJSBridgeHandlers() {
    if (inAppWebViewController != null) {
      messageHandlers.forEach((handlerName, jsBridgeHandler) {
        inAppWebViewController?.addJavaScriptHandler(handlerName: handlerName, callback: (List<dynamic> arguments) {
          LoggerManager().debug("inAppWebViewController.addJavaScriptHandler arguments:${arguments}");
          flutterFlushMessageQueue();
        });
      });
    }
  }

  // 移除注冊的方法
  void removeHandler(String handleName) {
    if (handleName.isNotEmpty) {
      messageHandlers.remove(handleName);
    }
  }

  // 重置,將responseCallbacks、startupMessageQueue重置
  void reset() {
    startupMessageQueue = [];
    responseCallbacks = {};
    _uniqueId = 0;
  }
}

InAppWebJSHandlerManager中使用inAppWebViewController?.addJavaScriptHandler來處理接收H5端的JS消息,并且將處理回調返回給H5。
當收到H5消息,flutter根據callbackId將處理后的結果回調給H5。

四、InAppWebJSBridgeRegister:appBridge調用的方法,flutter注冊的方法

InAppWebJSBridgeRegister實現處理flutter注冊的方法,提供相應的方法,H5端JS可以方法調用。

// appBridge調用的方法,flutter注冊的方法
class InAppWebJSBridgeRegister {
  late InAppWebJSHandlerManager _inAppWebJSHandlerManager;

  // 支付
  final ChannelPayPlatform _channelPayPlatform = ChannelPayPlatform();

  // 打開app等
  final ChannelLauncher _channelLauncher = ChannelLauncher();

  // 彈窗
  final ChannelDialog _channelDialog = ChannelDialog();

  // 掃碼或者識別二維碼
  final ChannelQrScanner _channelQrScanner = ChannelQrScanner();

  InAppWebJSBridgeRegister({required InAppWebJSHandlerManager inAppWebJSHandlerManager}) {
    _inAppWebJSHandlerManager = inAppWebJSHandlerManager;
  }

  // 注冊handlers
  void registerHandlers({JSChannelRegisterHandler? jsChannelRegisterHandler}) {
    // 設置標題
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.setTitle,
            (data, responseCallback) {
          if (data != null && data is String) {
            String title = data;
            if (jsChannelRegisterHandler != null) {
              jsChannelRegisterHandler(JSChannelRegisterMethod.setTitle, title);
            }
          }
        });

    // 獲取用戶昵稱
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getUsername,
            (data, responseCallback) {
          UserModel userModel =
          Provider.of<UserModel>(OneContext().context!, listen: false);
          String userNickName = userModel.userNickName ?? "";
          if (responseCallback != null) {
            responseCallback(userNickName);
          }
        });

    // 獲取定位
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getLoc,
            (data, responseCallback) {
          // TODO 獲取定位
        });

    // 獲取App名稱
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAppName,
            (data, responseCallback) {
          PackageInfo.fromPlatform().then((packageInfo) {
            String appName = "${packageInfo.appName}";
            if (responseCallback != null) {
              responseCallback(appName);
            }
          });
        });

    // 獲取版本號
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getVersion,
            (data, responseCallback) {
          PackageInfo.fromPlatform().then((packageInfo) {
            String version = "${packageInfo.buildNumber}";
            String versionCode = version.replaceAll(".", "");
            if (responseCallback != null) {
              responseCallback(versionCode);
            }
          });
        });

    // 獲取用戶id
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getUserId,
            (data, responseCallback) {
          UserModel userModel =
          Provider.of<UserModel>(OneContext().context!, listen: false);
          String userId = userModel.userId ?? "";
          if (responseCallback != null) {
            responseCallback(userId);
          }
        });

    // 獲取用戶登錄認證token
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAuthorization,
            (data, responseCallback) {
          UserModel userModel =
          Provider.of<UserModel>(OneContext().context!, listen: false);
          String token = userModel.token ?? "";
          if (responseCallback != null) {
            responseCallback(token);
          }
        });

    // 調用支付(微信支付/支付寶支付)原生
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.setPayPlatform,
            (data, responseCallback) {
          _channelPayPlatform.openUniPay(data, responseCallback);
        });

    // 打開掃一掃
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.openScan,
            (data, responseCallback) {
          // 打開掃一掃界面
          _channelQrScanner.openScanner(
              JSChannelRegisterMethod.openScan, data, responseCallback);
        });

    // 打開掃一掃
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.scanQrCode,
            (data, responseCallback) {
          // 打開掃一掃界面
          _channelQrScanner.openScanner(
              JSChannelRegisterMethod.scanQrCode, data, responseCallback);
        });

    // 打系統(tǒng)電話
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.callTelPhone,
            (data, responseCallback) {
          _channelLauncher.openLauncher(
              JSChannelRegisterMethod.callTelPhone, data, responseCallback);
        });

    // 發(fā)送短信
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.sendSms,
            (data, responseCallback) {
          _channelLauncher.openLauncher(
              JSChannelRegisterMethod.sendSms, data, responseCallback);
        });

    // 對話框 showDialog
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.showDialog,
            (data, responseCallback) {
          _channelDialog.openShowDialog(data, responseCallback);
        });

    // 底部選擇框
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.showCheckBox,
            (data, responseCallback) {
          _channelDialog.openShowSheetBox(data, responseCallback);
        });

    // 保存圖片到相冊
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.saveImage,
            (data, responseCallback) {
          // 保存圖片到相冊
          if (data != null && data is String && data.isNotEmpty) {
            FlutterLoadingHud.showLoading(message: "保存中...");
            SaveToAlbumUtil.saveImage(data, onCallback: (bool result, String message) {
              FlutterLoadingHud.dismiss();
              if (result) {
                // 保存成功
                FlutterLoadingHud.showToast(message: message);
              } else {
                // 保存失敗
                FlutterLoadingHud.showToast(message: message);
              }
            });
          }
        });

    // 識別二維碼
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.detectorQRCode,
            (data, responseCallback) {
          // 識別圖片中的二維碼
          _channelQrScanner.openScanner(
              JSChannelRegisterMethod.detectorQRCode, data, responseCallback);
        });

    // 打開App
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.openApp,
            (data, responseCallback) {
          _channelLauncher.openLauncher(
              JSChannelRegisterMethod.openApp, data, responseCallback);
        });

    // log
    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.log,
            (data, responseCallback) {
          Map<String, dynamic> dataJson = jsonDecode(data);
          int loggerType = dataJson["logType"];
          String message = dataJson["message"];
          if (LoggerMode.debug == loggerType) {
            LoggerManager().debug("registerHandlers log data: ${message}");
          } else if (LoggerMode.verbose == loggerType) {
            LoggerManager().verbose("registerHandlers log data: ${message}");
          } else if (LoggerMode.info == loggerType) {
            LoggerManager().info("registerHandlers log data: ${message}");
          } else if (LoggerMode.warning == loggerType) {
            LoggerManager().warning("registerHandlers log data: ${message}");
          } else if (LoggerMode.error == loggerType) {
            LoggerManager().error("registerHandlers log data: ${message}");
          }
        });


    _inAppWebJSHandlerManager.addJSBridgeHandlers();
  }

  // 處理是否跳轉,true可跳轉,false不可跳轉
  bool shouldOverrideUrlLoading(Uri uri) {
    ///在頁面跳轉之前調用 isForMainFrame為false,頁面不跳轉.導致網頁內很多鏈接點擊沒效果
    String url = uri.toString();
    return webViewNavigationAllowed(url);
  }

  // 處理是否跳轉,true可跳轉,false不可跳轉
  bool webViewNavigationAllowed(String url) {
    LoggerManager().debug('navigationDelegate decode $url');
    String telPrefix = "tel://";
    String smsPrefix = "sms://";
    String appPrefix = "app://";
    if (url.startsWith(telPrefix)) {
      String data = url.substring(telPrefix.length);
      _channelLauncher.openLauncher(
          JSChannelRegisterMethod.callTelPhone, data, null);
      // 不可跳轉
      return false;
    }

    if (url.startsWith(smsPrefix)) {
      String data = url.substring(smsPrefix.length);
      _channelLauncher.openLauncher(
          JSChannelRegisterMethod.sendSms, data, null);
      // 不可跳轉
      return false;
    }

    if (url.startsWith(appPrefix)) {
      // app://close
      _channelLauncher.openappUrl(url);
      return false;
    }

    if (url == "about:blank") {
      // 空頁面進行跳轉
      return true;
    }

    // 可跳轉
    return true;
  }
}


五、定義JSMessage:H5與flutter交互的消息體

class JSMessage {
  // {handlerName: getSessionID, data: , callbackId: cb_2_1665631238605}
  // handlerName
  String? handlerName;

  // data
  // flutter發(fā)送給H5的data,參數
  dynamic? data;

  /// callbackId,
  /// H5發(fā)送給flutter的callbackId,
  /// flutter處理后將調用 AppJSBridge._handleMessageFromObjC('%@');
  /// H5從responseCallbacks中根據callbackId找到callback回調方法進行執(zhí)行
  String? callbackId;

  /// responseId
  /// flutter發(fā)送給H5的responseId,
  /// responseId和callbackId是一樣的
  /// 如果是H5調用flutter時候,從H5過來的callbackId作為responseId回調給H5
  /// 如果是flutter調用H5,從flutter過來的callbackId作為responseId回調給flutter
  String? responseId;

  /// 回調的數據
  /// 如果是H5調用flutter時候,從flutter傳給H5的responseData作為回調數據
  /// 如果是flutter調用H5,從H5傳給flutter的responseData作為回調數據
  dynamic? responseData;

  JSMessage();

  JSMessage.fromJson(Map<String, dynamic> json) {
    callbackId = json['callbackId'];
    data = json['data'];
    handlerName = json['handlerName'];
    responseId = json['responseId'];
    responseData = json['responseData'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['callbackId'] = this.callbackId;
    data["data"] = this.data;
    data["handlerName"] = this.handlerName;
    data['responseId'] = this.responseId;
    data['responseData'] = this.responseData;
    return data;
  }

  void copy({required JSMessage aNewMessage, required JSMessage aOldMessage}) {
    aNewMessage.callbackId = aOldMessage.callbackId;
    aNewMessage.data = aOldMessage.data;
    aNewMessage.handlerName = aOldMessage.handlerName;
    aNewMessage.responseId = aOldMessage.responseId;
    aNewMessage.responseData = aOldMessage.responseData;
  }
}



六、封裝inwebview中使用InAppWebJSBridgeRegister、InAppWebJSHandlerManager

我們封裝inwebview中,使用InAppWebJSBridgeRegister、InAppWebJSHandlerManager。

定義InAppWebJSHandlerManager

// JS與Flutter調用的message Queue
  final InAppWebJSHandlerManager _inAppWebJSHandlerManager =
      InAppWebJSHandlerManager();

定義InAppWebJSBridgeRegister

// flutter注冊供H5調用的方法
  late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;

_inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(
        inAppWebJSHandlerManager: _inAppWebJSHandlerManager);

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

_inAppWebJSHandlerManager.updateController(
                context: context,
                inAppWebViewController: webViewController,
              );

在onWebViewCreated回調中注入ready代碼

              // 注入jsReady
              _inAppWebJSHandlerManager.injectJavascriptReady();

在onWebViewCreated回調中注冊flutter端的方法,

              // register a JavaScript handler with name "myHandlerName"
              _inAppWebJSBridgeRegister.registerHandlers(
                  jsChannelRegisterHandler: (handlerName, data) {
                if (JSChannelRegisterMethod.setTitle == handlerName) {
                  setWebPageTitle(data);
                }
              });

在onLoadStop回調中注入kInAppWebViewJavascriptBridge

            onLoadStop: (controller, url) async {
              // 注入
              _inAppWebJSHandlerManager.injectBridgeJavascript();

              _inAppWebJSHandlerManager.checkJavascriptBridge();

              // 加載完成
              widget.onLoadFinished(url.toString());
            },

完整代碼如下

class WebViewInAppScreen extends StatefulWidget {
  const WebViewInAppScreen({
    Key? key,
    required this.url,
    this.onWebProgress,
    this.onWebResourceError,
    required this.onLoadFinished,
    required this.onWebTitleLoaded,
    this.onWebViewCreated,
  }) : super(key: key);

  final String url;
  final Function(int progress)? onWebProgress;
  final Function(String? errorMessage)? onWebResourceError;
  final Function(String? url) onLoadFinished;
  final Function(String? webTitle)? onWebTitleLoaded;
  final Function(InAppWebViewController controller)? onWebViewCreated;

  @override
  State<WebViewInAppScreen> createState() => _WebViewInAppScreenState();
}

class _WebViewInAppScreenState extends State<WebViewInAppScreen> {
  final GlobalKey webViewKey = GlobalKey();

  InAppWebViewController? webViewController;
  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
    crossPlatform: InAppWebViewOptions(
      useShouldOverrideUrlLoading: true,
      mediaPlaybackRequiresUserGesture: false,
      applicationNameForUserAgent: "app-webview",
    ),
    android: AndroidInAppWebViewOptions(
      useHybridComposition: true,
    ),
    ios: IOSInAppWebViewOptions(
      allowsInlineMediaPlayback: true,
    ),
  );

  // JS與Flutter調用的message Queue
  final InAppWebJSHandlerManager _inAppWebJSHandlerManager =
      InAppWebJSHandlerManager();

  // cookie
  final InAppWebJSCookieConfig _inAppWebViewJSCookieConfig =
      InAppWebJSCookieConfig();

  // flutter注冊供H5調用的方法
  late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;

  bool _isDisposed = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _isDisposed = false;

    _inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(
        inAppWebJSHandlerManager: _inAppWebJSHandlerManager);
  }

  @override
  void dispose() {
    // TODO: implement dispose
    _isDisposed = true;
    _inAppWebJSHandlerManager.reset();
    webViewController?.clearCache();
    // _inAppWebViewJSCookieConfig.clear();
    super.dispose();
  }

  // 設置頁面標題
  void setWebPageTitle(data) {
    if (widget.onWebTitleLoaded != null) {
      widget.onWebTitleLoaded!(data);
    }
  }

  // flutter調用H5方法
  void callJSMethod() {
    _inAppWebJSHandlerManager.callHandler("JSAPPHandler",
        data: {"id": "a18c9fe0d"}, responseCallback: (dynamic responseData) {
      LoggerManager().debug("callJSMethod responseData:${responseData}");
      FlutterLoadingHud.showToast(message: jsonEncode(responseData));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Expanded(
          child: InAppWebView(
            key: webViewKey,
            initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),
            initialUserScripts: UnmodifiableListView<UserScript>([
              UserScript(
                  source:
                      "document.cookie='token=${ApiAuth.getToken()};domain='.inice.cn';path=/'",
                  injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START),
            ]),
            initialOptions: options,
            onWebViewCreated: (controller) {
              webViewController = controller;
              _inAppWebViewJSCookieConfig.setCookie(widget.url);
              _inAppWebJSHandlerManager.updateController(
                context: context,
                inAppWebViewController: webViewController,
              );
              // 注入jsReady
              _inAppWebJSHandlerManager.injectJavascriptReady();

              // register a JavaScript handler with name "myHandlerName"
              _inAppWebJSBridgeRegister.registerHandlers(
                  jsChannelRegisterHandler: (handlerName, data) {
                if (JSChannelRegisterMethod.setTitle == handlerName) {
                  setWebPageTitle(data);
                }
              });

              String filePre = "file://";
              if (widget.url.startsWith(filePre)) {
                String html = widget.url.substring(filePre.length);
                webViewController?.loadFile(
                    assetFilePath: 'assets/htmls/${html}');
              } else {
                if (widget.url.startsWith("http://") ||
                    widget.url.startsWith("https://")) {
                  webViewController?.loadUrl(
                      urlRequest: URLRequest(url: Uri.parse(widget.url)));
                }
              }

              if (widget.onWebViewCreated != null) {
                widget.onWebViewCreated!(controller);
              }
            },
            onTitleChanged: (controller, title) {
              if (widget.onWebTitleLoaded != null) {
                widget.onWebTitleLoaded!(title);
              }
            },
            onLoadStart: (controller, url) {},
            androidOnPermissionRequest: (controller, origin, resources) async {
              return PermissionRequestResponse(
                  resources: resources,
                  action: PermissionRequestResponseAction.GRANT);
            },
            shouldOverrideUrlLoading: (controller, navigationAction) async {
              var uri = navigationAction.request.url!;
              bool canNavigate =
                  _inAppWebJSBridgeRegister.shouldOverrideUrlLoading(uri);
              // 允許路由替換
              return canNavigate
                  ? NavigationActionPolicy.ALLOW
                  : NavigationActionPolicy.CANCEL;
            },
            onLoadStop: (controller, url) async {
              // 注入
              _inAppWebJSHandlerManager.injectBridgeJavascript();

              _inAppWebJSHandlerManager.checkJavascriptBridge();

              // 加載完成
              widget.onLoadFinished(url.toString());
            },
            onProgressChanged: (controller, progress) {
              if (widget.onWebProgress != null) {
                widget.onWebProgress!(progress);
              }
            },
            onLoadError: (controller, Uri? url, int code, String message) {
              if (widget.onWebResourceError != null) {
                widget.onWebResourceError!(message);
              }
            },
            onUpdateVisitedHistory: (controller, url, androidIsReload) {
              print("onUpdateVisitedHistory:${url}");
            },
            onConsoleMessage: (controller, consoleMessage) {
              print("consoleMessage:${consoleMessage}");
            },
          ),
        ),
        Container(
          height: ScreenUtil().bottomBarHeight + 50.0,
          color: Colors.white,
          child: Column(
            children: [
              Expanded(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: <Widget>[
                    ElevatedButton(
                      child: Icon(Icons.arrow_back),
                      onPressed: () {
                        webViewController?.goBack();
                      },
                    ),
                    SizedBox(
                      width: 25.0,
                    ),
                    ElevatedButton(
                      child: Icon(Icons.arrow_forward),
                      onPressed: () {
                        webViewController?.goForward();
                      },
                    ),
                    SizedBox(
                      width: 25.0,
                    ),
                    ElevatedButton(
                      child: Icon(Icons.refresh),
                      onPressed: () {
                        // callJSMethod();
                        webViewController?.reload();
                      },
                    ),
                  ],
                ),
              ),
              Container(
                height: ScreenUtil().bottomBarHeight,
              ),
            ],
          ),
        ),
      ],
    );
  }
}

七、使用inappwebview的page

最后,我們使用inappwebview,使用一個頁面打開對應的需要的鏈接地址,這里使用的本地測試頁面

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

  final Object? arguments;

  @override
  State<InAppWebViewPage> createState() => _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  String title = "";
  String? url;

  double webProgress = 0.0;

  @override
  void initState() {
    // TODO: implement initState
    if (widget.arguments != null && widget.arguments is Map) {
      Map obj = widget.arguments as Map;
      url = obj["url"];
    }

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

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

    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: WebAppBar(
        toolbarHeight: 44.0,
        backgroundColor: Theme.of(context).primaryColor,
        centerWidget: Text(
          "${title}",
          textAlign: TextAlign.center,
          overflow: TextOverflow.ellipsis,
          style: TextStyle(
            fontSize: 17,
            color: ColorUtil.hexColor(0xffffff),
            fontWeight: FontWeight.w600,
            fontStyle: FontStyle.normal,
            decoration: TextDecoration.none,
          ),
        ),
        leadingWidget: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            IconButton(
              padding: EdgeInsets.all(0.0),
              onPressed: () {
                navigatorBack();
              },
              icon: Icon(
                Icons.close_rounded,
                color: Colors.white,
                size: 30.0,
              ),
            ),
          ],
        ),
        trailingWidget: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            SizedBox(
              width: 28.0,
            ),
            SizedBox(
              width: 28.0,
            ),
          ],
        ),
      ),
      body: Stack(
        children: [
          WebViewInAppScreen(
            url: url ?? "",
            onWebProgress: (int progress) {
              if (mounted) {
                // TODO onWebProgress
                double precent = progress / 100.0;
                if (precent > 1.0) {
                  precent = 1.0;
                }

                if (precent < 0.0) {
                  precent = 0.0;
                }

                setState(() {
                  webProgress = precent;
                  LoggerManager().debug("webProgress:${webProgress}");
                });
              }
            },
            onLoadFinished: (String? url) {
              if (mounted) {
                // TODO onLoadFinished
              }
            },
            onWebTitleLoaded: (String? webTitle) {
              if (mounted) {
                setState(() {
                  title = webTitle ?? "";
                });
              }
            },
          ),
          buildProgressIndicator(context),
        ],
      ),
    );
  }

  Widget buildProgressIndicator(BuildContext context) {
    return (webProgress != 1.0)
        ? LinearProgressIndicator(
            backgroundColor: Colors.transparent,
            valueColor: AlwaysStoppedAnimation(ColorUtil.hexColor(0x3b93ff)),
            value: webProgress,
            minHeight: 2,
          )
        : Container();
  }

  void navigatorBack() {
    NavigatorPageRouter.pop();
  }
}

到此,inappwebview實現flutter與Javascript的交互JSBridge基本內容已經完成了。
查看效果
flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge,移動開發(fā),flutter開發(fā)實戰(zhàn),flutter,flutter,javascript,交互,inappwebview,HTML5,JSBridge,webview
flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge,移動開發(fā),flutter開發(fā)實戰(zhàn),flutter,flutter,javascript,交互,inappwebview,HTML5,JSBridge,webview

八、小結

inappwebview實現flutter與Javascript的交互JSBridge。描述可能不是特別準確,請見諒。

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

學習記錄,每天不停進步。文章來源地址http://www.zghlxwxcb.cn/news/detail-713861.html

到了這里,關于flutter開發(fā)實戰(zhàn)-inappwebview實現flutter與Javascript的交互JSBridge的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • flutter開發(fā)實戰(zhàn)-MethodChannel實現flutter與iOS雙向通信

    flutter開發(fā)實戰(zhàn)-MethodChannel實現flutter與iOS雙向通信 最近開發(fā)中需要iOS與flutter實現通信,這里使用的MethodChannel 如果需要flutter與Android實現雙向通信,請看 https://blog.csdn.net/gloryFlow/article/details/132218837 這部分與https://blog.csdn.net/gloryFlow/article/details/132218837中的一致,這里實現一下

    2024年02月13日
    瀏覽(25)
  • flutter開發(fā)實戰(zhàn)-事件總線EventBus實現

    flutter開發(fā)實戰(zhàn)-事件總線EventBus實現 在開發(fā)中,經常會需要一個廣播機制,用以跨Widget事件通知。 事件總線 實現了訂閱者模式,訂閱者模式包含發(fā)布者和訂閱者兩種角色,可以通過事件總線來觸發(fā)事件和監(jiān)聽事件。 實現eventBus 在工程的pubspec.yaml引入庫 1.使用event_bus庫 創(chuàng)建一

    2024年02月15日
    瀏覽(27)
  • flutter開發(fā)實戰(zhàn)-MethodChannel實現flutter與原生Android雙向通信

    flutter開發(fā)實戰(zhàn)-MethodChannel實現flutter與原生Android雙向通信 最近開發(fā)中需要原生Android與flutter實現通信,這里使用的MethodChannel MethodChannel:用于傳遞方法調用(method invocation)。 通道的客戶端和宿主端通過傳遞給通道構造函數的通道名稱進行連接 一個應用中所使用的所有通道名稱

    2024年02月13日
    瀏覽(20)
  • flutter開發(fā)實戰(zhàn)-dio文件下載實現

    flutter開發(fā)實戰(zhàn)-dio文件下載實現

    flutter開發(fā)實戰(zhàn)-dio文件下載實現 在開發(fā)中,需要下載文件,這里使用的是dio dio 是一個強大的 Dart HTTP 請求庫,支持全局配置、Restful API、FormData、攔截器、 請求取消、Cookie 管理、文件上傳/下載、超時以及自定義適配器等。 在工程中pubspec.yaml引入dio 我們對dio進行封裝 文件下

    2024年02月11日
    瀏覽(26)
  • flutter開發(fā)實戰(zhàn)-Universal Links配置及flutter微信分享實現

    flutter開發(fā)實戰(zhàn)-Universal Links配置及flutter微信分享實現

    flutter開發(fā)實戰(zhàn)-Universal Links配置及flutter微信分享實現 在最近開發(fā)中碰到了需要實現微信分享,在iOS端需要配置UniversalLink,在分享使用fluwx插件來實現微信分享功能。 1.1、什么是UniversalLink Universal link 是Apple在iOS9推出的一種能夠方便的通過傳統(tǒng)HTTPS鏈接來啟動APP的功能,可以使

    2024年01月19日
    瀏覽(23)
  • flutter開發(fā)實戰(zhàn)-指紋、面容ID驗證插件實現

    flutter開發(fā)實戰(zhàn)-指紋、面容ID驗證插件實現

    flutter開發(fā)實戰(zhàn)-指紋、面容ID驗證插件實現 在iOS開發(fā)中,經常出現需要指紋、面容ID驗證的功能。 指紋、面容ID是一種基于用生物識別技術,通過掃描用戶的面部特征來驗證用戶身份。 在iOS中實現指紋、面容ID驗證功能,步驟如下 2.1 info.plist配置 在info.plist中配置允許訪問FAC

    2024年02月13日
    瀏覽(28)
  • flutter開發(fā)實戰(zhàn)-實現推送功能Push Notification

    flutter開發(fā)實戰(zhàn)-實現推送功能Push Notification

    flutter開發(fā)實戰(zhàn)-實現推送功能Push Notification 推送服務現在可以說是所有 App 的標配了,最近在Flutter工程項目上實現推送功能。flutter上實現推送功能需要依賴原生的功能,需要插件實現,這里使用的是極光推送的服務。 效果圖如下 在使用極光推送功能時,需要使用的是極光提

    2024年02月16日
    瀏覽(15)
  • flutter開發(fā)實戰(zhàn)-RepaintBoundary實現Widget截圖功能

    flutter開發(fā)實戰(zhàn)-RepaintBoundary實現Widget截圖功能

    flutter開發(fā)實戰(zhàn)-RepaintBoundary實現Widget截圖功能 在開發(fā)中,遇到需要使用截圖,像iOS可以截圖UIView獲取到UIImage,在flutter中可以使用RepaintBoundary實現截圖功能 相機拍攝的圖片: RepaintBoundary截圖后的圖片 RepaintBoundary是繪制邊界。 如果CustomPaint有子節(jié)點,為了避免子節(jié)點不必要的

    2024年02月15日
    瀏覽(33)
  • flutter開發(fā)實戰(zhàn)-實現首頁分類目錄入口切換功能

    flutter開發(fā)實戰(zhàn)-實現首頁分類目錄入口切換功能

    。 在開發(fā)中經常遇到首頁的分類入口,如美團的美食團購、打車等入口,左右切換還可以分頁更多展示。 在pubspec.yaml引入 由于我這里按照一頁8條展示,兩行四列展示格式。 當列表list傳入的控件時候,一共的頁數為 通過列表,一頁數量計算每一頁應該展示多少個按鈕。 一

    2024年02月14日
    瀏覽(27)
  • flutter開發(fā)實戰(zhàn)-flutter_spinkit實現多種風格進度指示器

    flutter開發(fā)實戰(zhàn)-flutter_spinkit實現多種風格進度指示器

    flutter開發(fā)實戰(zhàn)-flutter_spinkit實現多種風格進度指示器 最近開發(fā)過程中flutter_spinkit,這個擁有多種種風格加載指示器 引入flutter_spinkit 效果示例 代碼如下 flutter開發(fā)實戰(zhàn)-flutter_spinkit實現多種風格進度指示器. 學習記錄,每天不停進步。

    2024年02月14日
    瀏覽(33)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包