import 'dart:convert'; // import 'dart:html'; 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 { close('close'), gameExit('gameExit'), createGame('createGame'), wantToPlay('wantToPlay'), gameOver('gameOver'), uploadImage('upload_image'), soundRecord('sound_recording'), toRecharge('toRecharge'), playing('playing'), toRedDiamond('toRedDiamond'), gameType('gameType'), jumpToH5('jumpToH5'), toHomepage('toHomepage'), toMonthCardPay('toMonthCardPay'), checkGameState('checkGameState'), closeObserving('closeObserving'), translateRequest('translateRequest'), share('share'), //开播检测 checkStartBroadcaster('checkStartBroadcaster'), //直播间发言 taskLiveRoomChat('taskLiveRoomChat'), //直播间送礼 taskLiveRoomGift('taskLiveRoomGift'), //直播间其他任务 close taskLiveRoomOther('taskLiveRoomOther'), //跳转从业者申请 toApplyAdmissionPage('toApplyAdmissionPage'), //直播预约设置 shouLiveBookingPicker('shouLiveBookingPicker'), //完善个人信息 shouldCompleteProfile('shouldCompleteProfile'), //在直播间或聊天室停留观看n分钟 shouldWatchDuration('shouldWatchDuration'), //在直播间或聊天室发送n条公屏消息 shouldSendPublicMessage('shouldSendPublicMessage'), //在直播间或聊天室上麦互动n分钟 shouldMicInteraction('shouldMicInteraction'), //向任意用户,发送n条信息 shouldSendPrivateMessage('shouldSendPrivateMessage'), //发布n条动态 shouldPostFeed('shouldPostFeed'), //分享n次直播间或聊天室至任意平台 shouldShareRoom('shouldShareRoom'), //佩戴任意装扮 shouldWearDecoration('shouldWearDecoration'), //前往语音房 shouldGoToVoiceRoom('shouldGoToVoiceRoom'), // 恩爱节活动跳转选择好友 lovingDayChooseFriend('lovingDayChooseFriend'), // 恩爱节我的邀请页面 lovingDayMyInvite('lovingDayMyInvite'), // 常驻cp活动规则页面 taskKeyToCPRule('taskKeyToCPRule'), // 定向充值 rechargeItem('rechargeItem'), //专属见面礼 shouldInviteCodeGift('InviteCodeGiftPackageDialogPage'), //解析 URL 并跳转 showCommandJump('commandJump'), //跳转至网页 showToWebViewPage('toWebViewPage'), ///通用交互 别往这下面加,👆🏻加 commonInteraction('commonInteraction'), defaultCode(''); const ToFlutterAppEnum(this.code); final String code; static ToFlutterAppEnum? fromCode(String code) { return ToFlutterAppEnum.values.firstWhere( (e) => e.code == code, orElse: () => ToFlutterAppEnum.defaultCode, ); } } //通用交互 enum WebInteractionType { taskKeyWatchLive('TaskKeyWatchLive'), // 观看直播 taskKeyCollectRoom('TaskKeyCollectRoom'), // 收藏房间 taskKeyFollowUser('TaskKeyFollowUser'), // 关注主播 taskKeySendRoomMessage('TaskKeySendRoomMessage'), // 发送房间消息 taskKeySendGift('TaskKeySendGift'), // 赠送礼物 taskKeySendGiftId('TaskKeySendGiftId'), // 赠送指定礼物 taskKeySendBackpackGift('TaskKeySendBackpackGift'), // 赠送礼物到背包 taskKeyPlayGame('TaskKeyPlayGame'), // 玩游戏 taskKeySendPrivateMessage('TaskKeySendPrivateMessage'), // 发送私聊消息 taskKeyShareActivity('TaskKeyShareActivity'), // 分享活动 taskKeyTimelineTopic('TaskKeyTimelineTopic'), // 参与动态话题 taskKeyLikeTimeline('TaskKeyLikeTimeline'), // 点赞动态 taskKeyReplayTimeline('TaskKeyReplayTimeline'), // 评论动态 taskKeySignIn('TaskKeySignIn'), // 签到 taskKeyOtherPage('TaskKeyOtherPage'), // 其他页面 taskKeyMineBackpack('TaskKeyMineBackpack'), // 我的装扮背包页面(或者是称号) taskKeyMineWallet('TaskKeyMineWallet'), // 我的钱包页面 unknown('unknown'); const WebInteractionType(this.code); final String code; //服务端 key factory WebInteractionType.fromCode(String? code) => values.firstWhere( (element) => element.code == code, orElse: () => WebInteractionType.unknown, ); } // 原生交互通知 h5 {type:'enum',data:'所需参数 '} enum FromFlutterAppEnum { translateResult('translateResult'), //翻译 redDiamondRecharge('diamond_recharge'), //钻石充值 shareFinished('share_finished'), //分享完成 cpSendRingCallBack('cp_send_ring_callback'), //送戒指后回调 defaultCode(''); const FromFlutterAppEnum(this.code); final String code; } @JS() external void sendMessageToNative(String data); /// 🆕 [新增] 通用请求任务模型 class _RequestTask { final String sendType; // 发送给 Native 的类型 final Map params; // 发送的参数 final Function(Map) callback; // 成功回调 _RequestTask(this.sendType, this.params, this.callback); } class FlutterBridge { static final FlutterBridge instance = FlutterBridge._internal(); // final _messageListeners = )>{}; // 1. 修改这里:Value 从 Function 变成 List final _messageListeners = )>>{}; var textMap = {}; //多语言翻译 /// 🆕 [新增] 通用请求队列池 (Key: listenType, Value: 任务列表) final Map> _requestQueues = {}; /// 🆕 [新增] 忙碌状态池 (记录哪些 listenType 当前正在等待 Native 回复) final Set _activeResponseTypes = {}; FlutterBridge._internal() { _initListener(); } // 只初始化一次,监听 WebView 消息 void _initListener() { html.window.onMessage.listen((event) { if (event.data is String) { try { final Map data = jsonDecode(event.data); final String? type = data['type']; // 找到监听列表,遍历调用 if (type != null && _messageListeners.containsKey(type)) { 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) { print('Invalid message format from Flutter: ${event.data}'); } } else { try { final String? type = event.data['type']; // 找到监听列表,遍历调用 if (type != null && _messageListeners.containsKey(type)) { 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) { print('Invalid message format from Flutter: ${event.data}'); } } }); } /// 3. 核心修改:on 方法 /// 返回一个 VoidCallback,调用它即可取消本次监听 VoidCallback on(String type, Function(Map) callback) { if (!_messageListeners.containsKey(type)) { _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); } } }; } /// 警告:这会移除该类型下的【所有】监听器 (慎用,通常用于全局重置,一旦调用,该 type 别的地方监听器将失效) void off(String type) { if (_messageListeners.containsKey(type)) { _messageListeners.remove(type); print("All listeners for '$type' have been removed."); } } // ========================================================= // 👇 🆕 [新增] 核心通用请求方法 (替代了旧的 requestTranslate) // ========================================================= /// 发送请求并等待回调 (自动排队,防并发) /// [sendType] 发送给 Native 的类型 /// [listenType] 等待 Native 回复的类型 /// [params] 参数 /// [onSuccess] 成功回调 void sendRequest({ required ToFlutterAppEnum sendType, required FromFlutterAppEnum listenType, required Map params, required Function(Map) onSuccess, }) { final responseKey = listenType.code; // 1. 初始化该类型的队列 if (!_requestQueues.containsKey(responseKey)) { _requestQueues[responseKey] = []; } // 2. 入队 _requestQueues[responseKey]!.add(_RequestTask( sendType.code, params, onSuccess, )); // 3. 调度执行 _processRequestQueue(responseKey); } /// 🆕 [新增] 内部队列调度方法 void _processRequestQueue(String responseKey) { // 如果该类型正在忙,或者队列空了,直接返回 if (_activeResponseTypes.contains(responseKey) || (_requestQueues[responseKey]?.isEmpty ?? true)) { return; } // 1. 标记忙碌 _activeResponseTypes.add(responseKey); // 2. 取出队首任务 final task = _requestQueues[responseKey]!.removeAt(0); // 3. 注册临时监听 (使用 on 的多播特性) VoidCallback? cancelRef; cancelRef = on(responseKey, (data) { try { final innerData = data['data'] ?? {}; // 执行业务回调 task.callback(innerData); } catch (e) { print("Queue processing error: $e"); } finally { // --- 任务闭环 --- // A. 移除当前临时监听 (不影响全局监听) cancelRef?.call(); // B. 解除忙碌状态 _activeResponseTypes.remove(responseKey); // C. 递归处理下一个任务 _processRequestQueue(responseKey); } }); // 4. 发送 Native 消息 sendToFlutter(task.sendType, task.params); } // 发送消息给 App(通过 WebView 调用 JS 方法) void sendToFlutter(String type, Map data) { final dataStr = jsonEncode({'type': type, 'data': data}); print('$dataStr'); sendMessageToNative(dataStr); } void sendToFlutterTest() { // final message = jsonEncode({ // 'type': type, // 'data': data, // }); // _runJs("receiveMessageFromFlutter($message);"); final dataStr = jsonEncode({'type': close, 'data': {}}); sendMessageToNative(dataStr); } // 执行 JS 代码 // void _runJs(String js) { // final script = ScriptElement() // ..innerHtml = js // ..type = 'application/javascript'; // document.body?.append(script); // script.remove(); // } // 具体封装的常用方法,直接发送消息给 Flutter Web void close() => sendToFlutter(ToFlutterAppEnum.close.code, {}); void gameOver() => sendToFlutter(ToFlutterAppEnum.gameOver.code, {}); void createGame(String gameId) => sendToFlutter(ToFlutterAppEnum.createGame.code, {'gameId': gameId}); void wantToPlay(String gameId) => sendToFlutter(ToFlutterAppEnum.wantToPlay.code, {'gameId': gameId}); // void translateRequest(Map data) => // sendToFlutter(ToFlutterAppEnum.translateRequest.code, data);//移除,建议使用sendRequest方法 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}, ); 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 checkGameState(String gameCode) => sendToFlutter( 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() ?? {}, }); //专属见面礼 void taskInviteCodeGift() => sendToFlutter(ToFlutterAppEnum.shouldInviteCodeGift.code, {}); //解析 URL 并跳转 void taskCommandJump({required String scheme, required String 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 checkStartBroadcaster() => sendToFlutter(ToFlutterAppEnum.checkStartBroadcaster.code, {}); void taskLiveRoomChat() => sendToFlutter(ToFlutterAppEnum.taskLiveRoomChat.code, {}); void taskLiveRoomGift() => sendToFlutter(ToFlutterAppEnum.taskLiveRoomGift.code, {}); void taskLiveRoomOther() => sendToFlutter(ToFlutterAppEnum.taskLiveRoomOther.code, {}); void toApplyAdmissionPage() => sendToFlutter(ToFlutterAppEnum.toApplyAdmissionPage.code, {}); void shouLiveBookingPicker() => sendToFlutter(ToFlutterAppEnum.shouLiveBookingPicker.code, {}); /** 完善个人信息 */ void shouldCompleteProfile() => sendToFlutter(ToFlutterAppEnum.shouldCompleteProfile.code, {}); /** 在直播间或聊天室停留观看n分钟 */ void shouldWatchDuration() => sendToFlutter(ToFlutterAppEnum.shouldWatchDuration.code, {}); /** 在直播间或聊天室发送n条公屏消息 */ void shouldSendPublicMessage() => sendToFlutter(ToFlutterAppEnum.shouldSendPublicMessage.code, {}); /** 在直播间或聊天室上麦互动n分钟 */ void shouldMicInteraction() => sendToFlutter(ToFlutterAppEnum.shouldMicInteraction.code, {}); /** 向任意用户,发送n条信息*/ void shouldSendPrivateMessage() => sendToFlutter(ToFlutterAppEnum.shouldSendPrivateMessage.code, {}); /** 发布n条动态*/ void shouldPostFeed() => sendToFlutter(ToFlutterAppEnum.shouldPostFeed.code, {}); /** 分享n次直播间或聊天室至任意平台 */ void shouldShareRoom() => sendToFlutter(ToFlutterAppEnum.shouldShareRoom.code, {}); /** 佩戴任意装扮 */ void shouldWearDecoration() => sendToFlutter(ToFlutterAppEnum.shouldWearDecoration.code, {}); /** 前往语音房 */ void shouldGoToVoiceRoom() => sendToFlutter(ToFlutterAppEnum.shouldGoToVoiceRoom.code, {}); /** 通用封装方法 无需传参可直接调用,需要传参需要调用sendToFlutter*/ void commonInteraction(Map data) => sendToFlutter(ToFlutterAppEnum.commonInteraction.code, data); }