diff --git a/lib/controller/overlay_controller.dart b/lib/controller/overlay_controller.dart index fe7f851..f6e8d28 100644 --- a/lib/controller/overlay_controller.dart +++ b/lib/controller/overlay_controller.dart @@ -2,22 +2,49 @@ import 'package:get/get.dart'; /// 全局 Overlay 控制器 class OverlayController extends GetxController { - /// overlay 是否显示 + /// overlay 是否显示(直播房间) final showOverlay = false.obs; - /// 显示 overlay + /// 视频通话 overlay 是否显示 + final showVideoCallOverlay = false.obs; + + /// 视频通话信息 + String? videoCallTargetUserId; + String? videoCallTargetUserName; + String? videoCallTargetAvatarUrl; + + /// 显示 overlay(直播房间) void show() { showOverlay.value = true; } - /// 隐藏 overlay + /// 隐藏 overlay(直播房间) void hide() { showOverlay.value = false; } - /// 切换 overlay 显示状态 + /// 切换 overlay 显示状态(直播房间) void toggle() { showOverlay.value = !showOverlay.value; } -} + /// 显示视频通话 overlay + void showVideoCall({ + required String targetUserId, + String? targetUserName, + String? targetAvatarUrl, + }) { + videoCallTargetUserId = targetUserId; + videoCallTargetUserName = targetUserName; + videoCallTargetAvatarUrl = targetAvatarUrl; + showVideoCallOverlay.value = true; + } + + /// 隐藏视频通话 overlay + void hideVideoCall() { + showVideoCallOverlay.value = false; + videoCallTargetUserId = null; + videoCallTargetUserName = null; + videoCallTargetAvatarUrl = null; + } +} diff --git a/lib/im/im_manager.dart b/lib/im/im_manager.dart index 1940974..224c538 100644 --- a/lib/im/im_manager.dart +++ b/lib/im/im_manager.dart @@ -243,9 +243,7 @@ class IMManager { // 如果获取到 revenueInfo,确保存储到消息的 attributes 中(用于UI显示) if (revenueInfo != null && revenueInfo.isNotEmpty) { - if (message.attributes == null) { - message.attributes = {}; - } + message.attributes ??= {}; // 将 revenueInfo 存储到 coin_value 中,以便UI组件可以直接使用 message.attributes!['coin_value'] = revenueInfo; @@ -1478,10 +1476,6 @@ class IMManager { final notification = _notificationQueue.removeAt(0); _isShowingNotification = true; - if (Get.isLogEnable) { - Get.log('✅ [IMManager] 显示消息通知弹框: fromId=${notification.fromId}, nickName=${notification.nickName}, 剩余队列长度=${_notificationQueue.length}'); - } - // 显示弹框(从上方弹出) SmartDialog.show( builder: (context) { @@ -1531,10 +1525,6 @@ class IMManager { _isShowingNotification = false; - if (Get.isLogEnable) { - Get.log('✅ [IMManager] 消息通知弹框已关闭,剩余队列长度=${_notificationQueue.length}'); - } - // 延迟一小段时间后显示下一条消息(确保动画完成) Future.delayed(Duration(milliseconds: 300), () { if (!_isShowingNotification) { @@ -1570,7 +1560,7 @@ class IMManager { } // 检查是否是GIFT消息 - if (content != null && content.startsWith('[GIFT:]')) { + if (content.startsWith('[GIFT:]')) { return '[礼物]'; } diff --git a/lib/main.dart b/lib/main.dart index 316c7a9..a3e2bc0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'package:dating_touchme_app/pages/mine/login_page.dart'; import 'package:dating_touchme_app/pages/setting/teenager_mode_page.dart'; import 'package:dating_touchme_app/rtc/rtc_manager.dart'; import 'package:dating_touchme_app/widget/live/draggable_overlay_widget.dart'; +import 'package:dating_touchme_app/widget/message/video_call_overlay_widget.dart'; import 'package:dating_touchme_app/widget/user_agreement_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -125,15 +126,27 @@ void main() async { try { if (Get.isRegistered()) { final overlayController = Get.find(); - return overlayController.showOverlay.value - ? DraggableOverlayWidget( - size: 60, - backgroundColor: const Color.fromRGBO(0, 0, 0, 0.6), - onClose: () { - overlayController.hide(); - }, - ) - : const SizedBox.shrink(); + // 视频通话小窗 + if (overlayController.showVideoCallOverlay.value) { + return VideoCallOverlayWidget( + targetUserId: overlayController.videoCallTargetUserId ?? '', + targetUserName: overlayController.videoCallTargetUserName, + targetAvatarUrl: overlayController.videoCallTargetAvatarUrl, + onClose: () { + overlayController.hideVideoCall(); + }, + ); + } + // 直播房间小窗 + if (overlayController.showOverlay.value) { + return DraggableOverlayWidget( + size: 60, + backgroundColor: const Color.fromRGBO(0, 0, 0, 0.6), + onClose: () { + overlayController.hide(); + }, + ); + } } } catch (e) { print('获取OverlayController失败: $e'); diff --git a/lib/pages/message/video_call_page.dart b/lib/pages/message/video_call_page.dart index 3fbf119..6489454 100644 --- a/lib/pages/message/video_call_page.dart +++ b/lib/pages/message/video_call_page.dart @@ -8,6 +8,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '../../controller/message/call_manager.dart'; import '../../controller/message/conversation_controller.dart'; +import '../../controller/overlay_controller.dart'; import '../../model/home/marriage_data.dart'; /// 视频通话页面 @@ -237,25 +238,32 @@ class _VideoCallPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.black, - body: Stack( - children: [ - // 背景视频/头像(模糊) - _buildBackground(), - - // 最小化按钮(左上角) - _buildMinimizeButton(), - - // 用户信息 - _buildUserInfo(), - - // 通话时长 - _buildCallDuration(), - - // 底部控制按钮 - _buildControlButtons(), - ], + return PopScope( + canPop: false, // 禁止手势返回 + onPopInvoked: (didPop) { + // 已经禁止返回,所以这里不会被调用 + // 如果需要返回,应该通过挂断按钮或其他明确的操作 + }, + child: Scaffold( + backgroundColor: Colors.black, + body: Stack( + children: [ + // 背景视频/头像(模糊) + _buildBackground(), + + // 最小化按钮(左上角) + _buildMinimizeButton(), + + // 用户信息 + _buildUserInfo(), + + // 通话时长 + _buildCallDuration(), + + // 底部控制按钮 + _buildControlButtons(), + ], + ), ), ); } @@ -274,8 +282,16 @@ class _VideoCallPageState extends State { /// 最小化通话 void _minimizeCall() { - // TODO: 实现最小化逻辑 - // 可以返回上一页,或者显示一个小窗口 + // 显示视频通话小窗 + if (Get.isRegistered()) { + final overlayController = Get.find(); + overlayController.showVideoCall( + targetUserId: widget.targetUserId, + targetUserName: _targetUserName, + targetAvatarUrl: _targetAvatarUrl, + ); + } + // 返回上一页 Get.back(); } diff --git a/lib/widget/live/live_room_guest_list_dialog.dart b/lib/widget/live/live_room_guest_list_dialog.dart index 116644c..4bd220f 100644 --- a/lib/widget/live/live_room_guest_list_dialog.dart +++ b/lib/widget/live/live_room_guest_list_dialog.dart @@ -627,15 +627,21 @@ class _LiveRoomGuestListDialogState extends State { final channelDetail = roomController.rtcChannelDetail.value; final anchorName = channelDetail?.anchorInfo?.nickName ?? '主持人'; final anchorAvatar = channelDetail?.anchorInfo?.profilePhoto ?? ''; + + // 确保头像和封面URL不为空且格式正确 + final cleanedAvatar = anchorAvatar.trim().replaceAll('`', ''); + final cleanedCover = cleanedAvatar; // 封面使用主持人头像 // 构建消息体,包含房间信息 final messageData = { 'type': 'live_room_invite', 'channelId': channelId, - 'anchorAvatar': anchorAvatar, + 'anchorAvatar': cleanedAvatar, 'anchorName': anchorName, - 'coverImage': anchorAvatar, // 封面使用主持人头像 + 'coverImage': cleanedCover, // 封面使用主持人头像 }; + + print('📤 [LiveRoomGuestListDialog] 发送房间邀请消息: anchorAvatar=$cleanedAvatar, coverImage=$cleanedCover'); // 发送自定义消息 final result = await IMManager.instance.sendCustomMessage( diff --git a/lib/widget/message/room_item.dart b/lib/widget/message/room_item.dart index 4c63705..7678e3d 100644 --- a/lib/widget/message/room_item.dart +++ b/lib/widget/message/room_item.dart @@ -70,8 +70,10 @@ class RoomItem extends StatelessWidget { String _getCoverImage() { final roomInfo = _parseRoomInfo(); // 优先使用封面图片,如果没有则使用头像 - final coverImage = roomInfo?['anchorAvatar'] ?? ''; - return coverImage.trim().replaceAll('`', ''); + final coverImage = roomInfo?['coverImage'] ?? roomInfo?['anchorAvatar'] ?? ''; + final cleanedUrl = coverImage.trim().replaceAll('`', ''); + print('📸 [RoomItem] 封面图片URL: $cleanedUrl'); + return cleanedUrl; } /// 处理点击事件 @@ -124,7 +126,7 @@ class RoomItem extends StatelessWidget { final anchorName = _getAnchorName(); final anchorAvatar = _getAnchorAvatar(); - final coverImage = _getAnchorAvatar(); + final coverImage = _getCoverImage(); return Column( children: [ @@ -209,6 +211,8 @@ class RoomItem extends StatelessWidget { }, ) : Container( + width: 150.w, + height: 150.w, color: Colors.grey[200], child: Icon( Icons.live_tv, @@ -269,27 +273,30 @@ class RoomItem extends StatelessWidget { width: 24.w, height: 24.w, fit: BoxFit.cover, - placeholder: (context, url) => - Container( - color: Colors.grey[300], - child: Center( - child: SizedBox( - width: 12.w, - height: 12.w, - child: CircularProgressIndicator( - strokeWidth: 2, - color: Colors.grey[600], - ), - ), + placeholder: (context, url) => Container( + width: 24.w, + height: 24.w, + color: Colors.grey[300], + child: Center( + child: SizedBox( + width: 12.w, + height: 12.w, + child: CircularProgressIndicator( + strokeWidth: 1.5, + color: Colors.grey[600], ), ), - errorWidget: (context, url, error) => - Image.asset( - Assets.imagesUserAvatar, - width: 24.w, - height: 24.w, - fit: BoxFit.cover, - ), + ), + ), + errorWidget: (context, url, error) { + print('❌ [RoomItem] 头像加载失败: $url, error: $error'); + return Image.asset( + Assets.imagesUserAvatar, + width: 24.w, + height: 24.w, + fit: BoxFit.cover, + ); + }, ) : Image.asset( Assets.imagesUserAvatar, diff --git a/pubspec.yaml b/pubspec.yaml index aea3178..7be9d96 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -108,8 +108,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - - assets/images/ - - assets/images/emoji/ + - build/app/outputs/flutter-apk/ # - images/a_dot_ham.jpeg # An images asset can refer to one or more resolution-specific "variants", see