From e54e11fef1898ce5c3116abf5bbc131725a0fa2c Mon Sep 17 00:00:00 2001 From: zhulixiao <1280253718@qq.com> Date: Tue, 27 Jan 2026 10:00:40 +0800 Subject: [PATCH] =?UTF-8?q?FlutterBridge=E7=9B=91=E5=90=AC=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=80=E5=AF=B9=E5=A4=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_bridge.dart | 160 ++++++++++++++++++++++++++------------ 1 file changed, 112 insertions(+), 48 deletions(-) diff --git a/lib/utils/app_bridge.dart b/lib/utils/app_bridge.dart index d70a8b3..2889bb6 100644 --- a/lib/utils/app_bridge.dart +++ b/lib/utils/app_bridge.dart @@ -4,6 +4,7 @@ import 'dart:html' as html; import 'package:js/js.dart'; import 'package:web_tools/utils/model/model.dart'; +import 'package:flutter/foundation.dart'; // h5交互通知 原生 {type:'enum',data:'所需参数,可无'} enum ToFlutterAppEnum { @@ -105,18 +106,15 @@ enum WebInteractionType { taskKeyOtherPage('TaskKeyOtherPage'), // 其他页面 taskKeyMineBackpack('TaskKeyMineBackpack'), // 我的装扮背包页面(或者是称号) taskKeyMineWallet('TaskKeyMineWallet'), // 我的钱包页面 - unknown('unknown'), - ; + unknown('unknown'); - const WebInteractionType( - this.code, - ); + const WebInteractionType(this.code); final String code; //服务端 key factory WebInteractionType.fromCode(String? code) => values.firstWhere( - (element) => element.code == code, - orElse: () => WebInteractionType.unknown, - ); + (element) => element.code == code, + orElse: () => WebInteractionType.unknown, + ); } // 原生交互通知 h5 {type:'enum',data:'所需参数 '} @@ -146,7 +144,9 @@ class FlutterBridge { // 取消监听 'translateResult' 类型的消息 FlutterBridge.instance.off(FromJsEnum.translateResult.code); * */ - final _messageListeners = )>{}; + // final _messageListeners = )>{}; + // 1. 修改这里:Value 从 Function 变成 List + final _messageListeners = )>>{}; var textMap = {}; //多语言翻译 FlutterBridge._internal() { @@ -160,8 +160,19 @@ class FlutterBridge { try { final Map data = jsonDecode(event.data); final String? type = data['type']; + // 找到监听列表,遍历调用 if (type != null && _messageListeners.containsKey(type)) { - _messageListeners[type]?.call(data); + final listeners = _messageListeners[type]; + if (listeners != null && listeners.isNotEmpty) { + // 使用 List.from 浅拷贝一份进行遍历,防止在回调中移除监听导致并发修改异常 + for (final callback in List.from(listeners)) { + try { + callback(data); // 这里的 data 是已经解析好的 Map + } catch (e) { + print("Error in listener callback: $e"); + } + } + } } print('Invalid message format from Flutter: $type'); } catch (e) { @@ -170,8 +181,19 @@ class FlutterBridge { } else { try { final String? type = event.data['type']; + // 找到监听列表,遍历调用 if (type != null && _messageListeners.containsKey(type)) { - _messageListeners[type]?.call(event.data); + final listeners = _messageListeners[type]; + if (listeners != null && listeners.isNotEmpty) { + // 使用 List.from 浅拷贝一份进行遍历,防止在回调中移除监听导致并发修改异常 + for (final callback in List.from(listeners)) { + try { + callback(event.data); // 这里的 data 是已经解析好的 Map + } catch (e) { + print("Error in listener callback: $e"); + } + } + } } print('Invalid message format from Flutter: $type'); } catch (e) { @@ -181,24 +203,56 @@ class FlutterBridge { }); } - // 注册监听某个 type 的消息 - void on(String type, Function(Map) callback) { - if (_messageListeners.containsKey(type)) { - print("Listener for '$type' is already registered."); - return; // 如果已经注册了这个类型的监听器,就不再重复添加 - } - _messageListeners[type] = callback; - print("Listener for '$type' has been registered."); + /// 仅监听一次,触发后自动移除 + void once(String type, Function(Map) callback) { + // 定义一个引用,用于在回调内部取消自己 + VoidCallback? cancelRef; + + // 注册监听 + cancelRef = on(type, (data) { + // 1. 立即移除监听 + cancelRef?.call(); + + // 2. 执行真正的业务回调 + callback(data); + }); } - // 取消监听某个 type 的消息 - void off(String type) { + /// 3. 核心修改:on 方法 + /// 返回一个 VoidCallback,调用它即可取消本次监听 + VoidCallback on(String type, Function(Map) callback) { if (!_messageListeners.containsKey(type)) { - print("No listener found for '$type'."); - return; // 如果没有找到监听器,直接返回 + _messageListeners[type] = []; + } + + _messageListeners[type]!.add(callback); + print( + "Listener added for '$type'. Total listeners: ${_messageListeners[type]!.length}", + ); + + // 返回一个闭包,用于精确移除当前的 callback + return () { + if (_messageListeners.containsKey(type)) { + _messageListeners[type]?.remove(callback); + print( + "One listener removed for '$type'. Remaining: ${_messageListeners[type]?.length}", + ); + + // 如果该类型没有监听者了,可以选择清理 key + if (_messageListeners[type]!.isEmpty) { + _messageListeners.remove(type); + } + } + }; + } + + /// 4. 兼容旧代码的 off 方法 + /// 警告:这会移除该类型下的【所有】监听器 + void off(String type) { + if (_messageListeners.containsKey(type)) { + _messageListeners.remove(type); + print("All listeners for '$type' have been removed."); } - _messageListeners.remove(type); - print("Listener for '$type' has been removed."); } // 发送消息给 App(通过 WebView 调用 JS 方法) @@ -238,29 +292,34 @@ class FlutterBridge { void toRecharge() => sendToFlutter(ToFlutterAppEnum.toRecharge.code, {}); void toRedDiamond() => sendToFlutter(ToFlutterAppEnum.toRedDiamond.code, {}); void jumpToH5(String path, String title) => sendToFlutter( - ToFlutterAppEnum.jumpToH5.code, {'path': path, 'title': title}); + ToFlutterAppEnum.jumpToH5.code, + {'path': path, 'title': title}, + ); void toHomepage(String userId) => sendToFlutter(ToFlutterAppEnum.toHomepage.code, {'userId': userId}); - void toMonthCardPay(String googleProductId, String iosProductId, - {otherUserId = ''}) => - sendToFlutter(ToFlutterAppEnum.toMonthCardPay.code, { - 'googleProductId': googleProductId, - 'iosProductId': iosProductId, - 'otherUserId': otherUserId, - }); + void toMonthCardPay( + String googleProductId, + String iosProductId, { + otherUserId = '', + }) => sendToFlutter(ToFlutterAppEnum.toMonthCardPay.code, { + 'googleProductId': googleProductId, + 'iosProductId': iosProductId, + 'otherUserId': otherUserId, + }); void checkGameState(String gameCode) => sendToFlutter( - ToFlutterAppEnum.checkGameState.code, {'gameCode': gameCode}); + ToFlutterAppEnum.checkGameState.code, + {'gameCode': gameCode}, + ); void share({ required String activityId, // 活动id required bool needShareReport, // 是否需要上报分享数据 ShareCardModel? shareCardModel, // 分享上方卡片 数据,参考客户端参数 - }) => - sendToFlutter(ToFlutterAppEnum.share.code, { - "activityId": activityId, - "shareReportKey": needShareReport ? "ActivityShared" : "", - ...shareCardModel?.toJson() ?? {}, - }); + }) => sendToFlutter(ToFlutterAppEnum.share.code, { + "activityId": activityId, + "shareReportKey": needShareReport ? "ActivityShared" : "", + ...shareCardModel?.toJson() ?? {}, + }); //专属见面礼 void taskInviteCodeGift() => @@ -268,14 +327,19 @@ class FlutterBridge { //解析 URL 并跳转 void taskCommandJump({required String scheme, required String activityId}) => - sendToFlutter(ToFlutterAppEnum.showCommandJump.code, - {"scheme": scheme, "activityId": activityId}); + sendToFlutter(ToFlutterAppEnum.showCommandJump.code, { + "scheme": scheme, + "activityId": activityId, + }); //跳转至网页 - void taskToWebViewPage( - {required String linkUrl, required String activityId}) => - sendToFlutter(ToFlutterAppEnum.showToWebViewPage.code, - {"linkUrl": linkUrl, "activityId": activityId}); + void taskToWebViewPage({ + required String linkUrl, + required String activityId, + }) => sendToFlutter(ToFlutterAppEnum.showToWebViewPage.code, { + "linkUrl": linkUrl, + "activityId": activityId, + }); void checkStartBroadcaster() => sendToFlutter(ToFlutterAppEnum.checkStartBroadcaster.code, {}); @@ -296,7 +360,7 @@ class FlutterBridge { /** 在直播间或聊天室停留观看n分钟 */ void shouldWatchDuration() => sendToFlutter(ToFlutterAppEnum.shouldWatchDuration.code, {}); -/** 在直播间或聊天室发送n条公屏消息 */ + /** 在直播间或聊天室发送n条公屏消息 */ void shouldSendPublicMessage() => sendToFlutter(ToFlutterAppEnum.shouldSendPublicMessage.code, {}); /** 在直播间或聊天室上麦互动n分钟 */ @@ -311,7 +375,7 @@ class FlutterBridge { /** 分享n次直播间或聊天室至任意平台 */ void shouldShareRoom() => sendToFlutter(ToFlutterAppEnum.shouldShareRoom.code, {}); -/** 佩戴任意装扮 */ + /** 佩戴任意装扮 */ void shouldWearDecoration() => sendToFlutter(ToFlutterAppEnum.shouldWearDecoration.code, {}); /** 前往语音房 */