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'),
reload('reload'), // 刷新
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 reload() => sendToFlutter(ToFlutterAppEnum.reload.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);
}