import 'package:dating_touchme_app/controller/discover/room_controller.dart'; import 'package:dating_touchme_app/controller/global.dart'; import 'package:dating_touchme_app/controller/mine/rose_controller.dart'; import 'package:dating_touchme_app/controller/overlay_controller.dart'; import 'package:dating_touchme_app/extension/ex_fuction.dart'; import 'package:dating_touchme_app/generated/assets.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:dating_touchme_app/widget/live/live_room_user_header.dart'; import 'package:dating_touchme_app/widget/live/live_room_anchor_showcase.dart'; import 'package:dating_touchme_app/widget/live/live_room_active_speaker.dart'; import 'package:dating_touchme_app/widget/live/live_room_notice_chat_panel.dart'; import 'package:dating_touchme_app/widget/live/live_room_action_bar.dart'; import 'package:dating_touchme_app/widget/live/live_gift_popup.dart'; import 'package:dating_touchme_app/widget/live/live_recharge_popup.dart'; import 'package:dating_touchme_app/widget/live/svga_player_widget.dart'; class LiveRoomPage extends StatefulWidget { final int id; const LiveRoomPage({super.key, required this.id}); @override State createState() => _LiveRoomPageState(); } class _LiveRoomPageState extends State { late final RoomController _roomController; late final OverlayController _overlayController; String message = ''; final TextEditingController _messageController = TextEditingController(); final TextEditingController _inputDialogController = TextEditingController(); final FocusNode _inputDialogFocusNode = FocusNode(); bool _showInputDialog = false; bool _isOpeningDialog = false; // 标记是否正在打开对话框 final activeGift = ValueNotifier(null); final giftNum = ValueNotifier(1); changeActive(int index) { activeGift.value = index; setState(() {}); } @override void initState() { super.initState(); _roomController = Get.isRegistered() ? Get.find() : Get.put(RoomController()); _overlayController = Get.find(); // 进入直播间时,确保隐藏小窗口(延迟到 build 完成后执行,避免在 build 过程中触发 setState) WidgetsBinding.instance.addPostFrameCallback((_) { if (_overlayController.showOverlay.value) { _overlayController.hide(); } }); // 监听输入对话框的焦点变化,当键盘收起时隐藏对话框 _inputDialogFocusNode.addListener(_onInputDialogFocusChanged); // 启用屏幕常亮 WakelockPlus.enable(); // 如果当前用户是男性,请求连麦卡片信息 _loadLinkMicCard(); // 请求玫瑰数量 _loadRoseCount(); } /// 加载连麦卡片信息(仅男性用户) Future _loadLinkMicCard() async { final userData = GlobalData().userData; if (userData?.genderCode == 0) { // 男性用户才请求 await _roomController.getUserPropLinkMicCard(); } } /// 加载玫瑰数量 Future _loadRoseCount() async { await _roomController.getVirtualAccount(); } /// 输入对话框焦点变化回调 void _onInputDialogFocusChanged() { if (!mounted) return; // 如果焦点丢失且对话框仍显示,检查键盘状态 if (!_inputDialogFocusNode.hasFocus && _showInputDialog) { // 使用 addPostFrameCallback 确保在下一帧检查,此时键盘状态已更新 WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; // 如果键盘已收起,隐藏对话框 final keyboardHeight = MediaQuery.of(context).viewInsets.bottom; if (keyboardHeight == 0) { _hideInputDialog(); } }); } } @override void dispose() { // 移除焦点监听器 _inputDialogFocusNode.removeListener(_onInputDialogFocusChanged); // 禁用屏幕常亮 WakelockPlus.disable(); _messageController.dispose(); _inputDialogController.dispose(); _inputDialogFocusNode.dispose(); SmartDialog.dismiss(); // 退出房间时清空RTM消息 if (Get.isRegistered()) { final roomController = Get.find(); roomController.chatMessages.clear(); } super.dispose(); } /// 显示输入对话框 void _openInputDialog() { _isOpeningDialog = true; setState(() { _showInputDialog = true; }); // 延迟获取焦点,确保对话框已显示 WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted && _showInputDialog) { _inputDialogFocusNode.requestFocus(); // 键盘弹起后,重置标志 Future.delayed(const Duration(milliseconds: 500), () { if (mounted) { _isOpeningDialog = false; } }); } }); } /// 隐藏输入对话框 void _hideInputDialog() { _isOpeningDialog = false; setState(() { _showInputDialog = false; }); _inputDialogFocusNode.unfocus(); _inputDialogController.clear(); } /// 发送输入对话框中的消息 void _sendInputDialogMessage() { final content = _inputDialogController.text.trim(); if (content.isEmpty) { return; } // 更新主 controller _messageController.text = content; message = content; // 发送消息 _sendMessage(); // 关闭对话框 _hideInputDialog(); } /// 发送消息 Future _sendMessage() async { final content = _messageController.text.trim(); if (content.isEmpty) { return; } // 发送消息 await _roomController.sendChatMessage(content); // 清空输入框 _messageController.clear(); message = ''; } void _showGiftPopup() async { if(_roomController.isDialogShowing.value){ return; } // 隐藏键盘 FocusScope.of(context).unfocus(); // 刷新玫瑰数量 await _roomController.getVirtualAccount(); _roomController.setDialogDismiss(true); SmartDialog.show( backType: SmartBackType.block, alignment: Alignment.bottomCenter, maskColor: TDTheme.of(context).fontGyColor2, onDismiss: () { _roomController.setDialogDismiss(false); }, builder: (_) => Obx(() { // 优先使用 API 返回的 giftProducts,如果为空则使用后备列表 final giftProducts = _roomController.giftProducts; final giftList = giftProducts.toList(); return LiveGiftPopup( activeGift: activeGift, giftNum: giftNum, giftList: giftList, changeActive: changeActive, ); }), ); } void _showRechargePopup() async { // 隐藏键盘 FocusScope.of(context).unfocus(); _roomController.setDialogDismiss(true); final roseController = Get.isRegistered() ? Get.find() : Get.put(RoseController()); await roseController.getRoseNum(); SmartDialog.show( alignment: Alignment.bottomCenter, maskColor: TDTheme.of(context).fontGyColor2, onDismiss: (){ _roomController.setDialogDismiss(false); }, builder: (_) => const LiveRechargePopup(), ); } @override Widget build(BuildContext context) { // 在 build 方法开始时立即隐藏小窗口(使用 addPostFrameCallback 避免在 build 过程中触发 setState) WidgetsBinding.instance.addPostFrameCallback((_) { if (_overlayController.showOverlay.value) { _overlayController.hide(); } // 检查键盘状态,如果键盘收起但对话框仍显示,且不是正在打开对话框,则隐藏对话框 if (MediaQuery.of(context).viewInsets.bottom == 0 && _showInputDialog && !_isOpeningDialog) { // 延迟一点时间再检查,避免在键盘弹起过程中误判 Future.delayed(const Duration(milliseconds: 300), () { if (mounted && MediaQuery.of(context).viewInsets.bottom == 0 && _showInputDialog && !_isOpeningDialog) { _hideInputDialog(); } }); } }); return Obx(() { return PopScope( canPop: !_roomController.isDialogShowing.value, onPopInvokedWithResult: (bool didPop, Object? result) { // SmartDialog.dismiss(); // print('256>22>>' + didPop.toString()); // if (didPop) return; // 如果有对话框显示,关闭对话框 if (_roomController.isDialogShowing.value) { SmartDialog.dismiss(); return; // 阻止页面返回 } // 退出房间时清空RTM消息 if (Get.isRegistered()) { final roomController = Get.find(); roomController.chatMessages.clear(); } // 如果还没有执行 pop,手动调用 Get.back() if (!didPop) { Get.back(); } // 等待页面关闭后再显示小窗口,确保小窗口能正确显示 Future.delayed(const Duration(milliseconds: 200), () { _overlayController.show(); }); }, child: Scaffold( resizeToAvoidBottomInset: false, body: Stack( children: [ Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color.fromRGBO(248, 242, 255, 1), Color.fromRGBO(247, 247, 247, 1), ], ), ), ), Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [ Color.fromRGBO(19, 16, 47, 1), Color.fromRGBO(19, 16, 47, 1), ], ), ), ), Container( padding: EdgeInsets.only( top: MediaQuery.of(context).padding.top, ), child: Column( children: [ SizedBox(height: 10.w), Obx(() { final detail = _roomController.rtcChannelDetail.value; final anchorInfo = detail?.anchorInfo; final userName = anchorInfo?.nickName ?? '用户'; final avatarAsset = anchorInfo?.profilePhoto ?? Assets.imagesUserAvatar; const popularityText = '0'; // TODO: 使用真实数据 return LiveRoomUserHeader( userName: userName, popularityText: popularityText, avatarAsset: avatarAsset, onCloseTap: () { SmartDialog.dismiss(); // 退出房间时清空RTM消息 if (Get.isRegistered()) { final roomController = Get.find(); roomController.chatMessages.clear(); } Get.back(); // 等待页面关闭后再显示小窗口,确保小窗口能正确显示 Future.delayed(const Duration(milliseconds: 200), () { _overlayController.show(); }); }, ); }), SizedBox(height: 7.w), LiveRoomAnchorShowcase(), SizedBox(height: 5.w), const LiveRoomActiveSpeaker(), SizedBox(height: 9.w), Expanded(child: const LiveRoomNoticeChatPanel()), // 根据键盘状态显示/隐藏 LiveRoomActionBar if (MediaQuery.of(context).viewInsets.bottom == 0) SafeArea( top: false, child: Padding( padding: EdgeInsets.only( bottom: 10.w, left: 0, right: 0, ), child: LiveRoomActionBar( messageController: _messageController, onMessageChanged: (value) { message = value; }, onSendTap: _sendMessage, onGiftTap: _showGiftPopup.throttle(), onChargeTap: _showRechargePopup, onInputTap: _openInputDialog, ), ), ), ], ), ), // SVGA 动画播放组件 const SvgaPlayerWidget(), // 输入对话框 if (_showInputDialog) ...[ // 遮罩层,点击时隐藏对话框和键盘(放在对话框下方,对话框会在上面拦截点击) Positioned.fill( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { // 隐藏键盘 FocusScope.of(context).unfocus(); // 隐藏对话框 _hideInputDialog(); }, child: Container(color: Colors.transparent), ), ), // 输入对话框(放在遮罩层上面,会自动拦截点击事件) AnimatedPositioned( duration: const Duration(milliseconds: 200), curve: Curves.easeOut, left: 0, right: 0, bottom: MediaQuery.of(context).viewInsets.bottom, child: _InputDialogWidget( controller: _inputDialogController, focusNode: _inputDialogFocusNode, onSend: _sendInputDialogMessage, onClose: _hideInputDialog, ), ), ], ], ), ), ); }); } } class _InputDialogWidget extends StatelessWidget { const _InputDialogWidget({ required this.controller, required this.focusNode, required this.onSend, required this.onClose, }); final TextEditingController controller; final FocusNode focusNode; final VoidCallback onSend; final VoidCallback onClose; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(20.w), topRight: Radius.circular(20.w), ), ), child: SafeArea( top: false, child: Padding( padding: EdgeInsets.all(16.w), child: Row( children: [ Expanded( child: Container( height: 38.w, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(38.w)), color: const Color.fromRGBO(245, 245, 245, 1), ), alignment: Alignment.centerLeft, child: TextField( controller: controller, focusNode: focusNode, keyboardType: TextInputType.text, textAlignVertical: TextAlignVertical.center, style: TextStyle( fontSize: ScreenUtil().setWidth(14), height: 1.2, color: Colors.black, ), decoration: InputDecoration( contentPadding: EdgeInsets.symmetric( vertical: 0, horizontal: 15.w, ), hintText: "说点什么", hintStyle: TextStyle( color: const Color.fromRGBO(144, 144, 144, 1), height: 1.2, fontSize: ScreenUtil().setWidth(14), ), border: InputBorder.none, isDense: true, ), onSubmitted: (_) { onSend(); }, ), ), ), SizedBox(width: 12.w), GestureDetector( onTap: onSend, child: Container( padding: EdgeInsets.symmetric( horizontal: 24.w, vertical: 10.h, ), decoration: BoxDecoration( color: const Color.fromRGBO(117, 98, 249, 1), borderRadius: BorderRadius.circular(19.w), ), child: Text( "发送", style: TextStyle( fontSize: ScreenUtil().setWidth(14), color: Colors.white, fontWeight: FontWeight.w500, ), ), ), ), ], ), ), ), ); } }