Browse Source

fix(gift): 修复礼物弹窗和直播间相关功能问题

- 修复聊天礼物弹窗底部安全区域适配问题
- 修复直播间礼物弹窗底部安全区域适配问题
- 修复直播间礼物弹窗用户选择验证逻辑
- 移除直播间礼物弹窗多余标签组件
- 修复直播间礼物弹窗赠送按钮状态管理
- 修复直播间邀请消息解析和显示问题
- 修复直播间封面图片URL清理逻辑
- 修复直播间邀请消息点击跳转逻辑
- 修复直播间消息列表退出时清空逻辑
- 修复直播间底部栏组件格式问题
master
Jolie 3 months ago
parent
commit
5315e75ae6
10 changed files with 285 additions and 198 deletions
  1. 4
      lib/controller/discover/room_controller.dart
  2. 5
      lib/controller/message/conversation_controller.dart
  3. 5
      lib/im/im_manager.dart
  4. 10
      lib/pages/discover/live_room_page.dart
  5. 20
      lib/pages/main/main_page.dart
  6. 157
      lib/widget/live/live_gift_popup.dart
  7. 38
      lib/widget/live/live_room_action_bar.dart
  8. 4
      lib/widget/live/live_room_guest_list_dialog.dart
  9. 83
      lib/widget/message/chat_gift_popup.dart
  10. 157
      lib/widget/message/room_item.dart

4
lib/controller/discover/room_controller.dart

@ -79,6 +79,8 @@ class RoomController extends GetxController with WidgetsBindingObserver {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
// //
_messageService.unregisterMessageListener(); _messageService.unregisterMessageListener();
// RTM消息列表
chatMessages.clear();
} }
/// ///
@ -348,6 +350,8 @@ class RoomController extends GetxController with WidgetsBindingObserver {
} }
currentRole = CurrentRole.normalUser; currentRole = CurrentRole.normalUser;
await RTCManager.instance.leaveChannel(); await RTCManager.instance.leaveChannel();
// RTM消息列表
chatMessages.clear();
} }
/// ///

5
lib/controller/message/conversation_controller.dart

@ -411,6 +411,11 @@ class ConversationController extends GetxController {
} }
} }
// GIFT消息
if (content != null && content.startsWith('[GIFT:]')) {
return '[礼物]';
}
return content ?? ''; return content ?? '';
}else if(message.body.type == MessageType.IMAGE){ }else if(message.body.type == MessageType.IMAGE){
return '[图片]'; return '[图片]';

5
lib/im/im_manager.dart

@ -1569,6 +1569,11 @@ class IMManager {
} }
} }
// GIFT消息
if (content != null && content.startsWith('[GIFT:]')) {
return '[礼物]';
}
return content ?? ''; return content ?? '';
} else if (message.body.type == MessageType.IMAGE) { } else if (message.body.type == MessageType.IMAGE) {
return '[图片]'; return '[图片]';

10
lib/pages/discover/live_room_page.dart

@ -54,6 +54,11 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
// //
WakelockPlus.disable(); WakelockPlus.disable();
_messageController.dispose(); _messageController.dispose();
// 退RTM消息
if (Get.isRegistered<RoomController>()) {
final roomController = Get.find<RoomController>();
roomController.chatMessages.clear();
}
super.dispose(); super.dispose();
} }
@ -108,6 +113,11 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
return PopScope( return PopScope(
onPopInvokedWithResult: (bool didPop, Object? result) async { onPopInvokedWithResult: (bool didPop, Object? result) async {
SmartDialog.dismiss(); SmartDialog.dismiss();
// 退RTM消息
if (Get.isRegistered<RoomController>()) {
final roomController = Get.find<RoomController>();
roomController.chatMessages.clear();
}
_overlayController.show(); _overlayController.show();
Get.back(); Get.back();
}, },

20
lib/pages/main/main_page.dart

@ -103,17 +103,17 @@ class _MainPageState extends State<MainPage> {
final unreadCount = controller.totalUnreadCount.value; final unreadCount = controller.totalUnreadCount.value;
return TDBottomTabBar( return TDBottomTabBar(
currentIndex: currentIndex,
TDBottomTabBarBasicType.iconText,
componentType: TDBottomTabBarComponentType.normal,
useVerticalDivider: false,
navigationTabs: [
tabItem('首页', Assets.imagesHomePre, Assets.imagesHomeNol, 0),
tabItem('找对象', Assets.imagesDiscoverPre, Assets.imagesDiscoverNol, 1),
currentIndex: currentIndex,
TDBottomTabBarBasicType.iconText,
componentType: TDBottomTabBarComponentType.normal,
useVerticalDivider: false,
navigationTabs: [
tabItem('首页', Assets.imagesHomePre, Assets.imagesHomeNol, 0),
tabItem('找对象', Assets.imagesDiscoverPre, Assets.imagesDiscoverNol, 1),
tabItemWithBadge('消息', Assets.imagesMessagePre, Assets.imagesMessageNol, 2, unreadCount), tabItemWithBadge('消息', Assets.imagesMessagePre, Assets.imagesMessageNol, 2, unreadCount),
tabItem('我的', Assets.imagesMinePre, Assets.imagesMineNol, 3),
]
);
tabItem('我的', Assets.imagesMinePre, Assets.imagesMineNol, 3),
]
);
}); });
} }

157
lib/widget/live/live_gift_popup.dart

@ -76,11 +76,18 @@ class _LiveGiftPopupState extends State<LiveGiftPopup> {
} }
// showHeader为falsetargetUserId // showHeader为falsetargetUserId
if (_selectedUserId == 0) {
if (widget.showHeader) {
if (widget.showHeader) {
//
if (_selectedUserId == null || _selectedUserId == 0) {
SmartDialog.showToast('请先选择接收礼物的用户'); SmartDialog.showToast('请先选择接收礼物的用户');
return;
}
} else {
// 使targetUserIdtargetUserId也没有_selectedUserId
if (widget.targetUserId == null && (_selectedUserId == null || _selectedUserId == 0)) {
SmartDialog.showToast('请先选择接收礼物的用户');
return;
} }
return;
} }
// //
@ -133,7 +140,6 @@ class _LiveGiftPopupState extends State<LiveGiftPopup> {
), ),
child: Column( child: Column(
children: [ children: [
_buildTab(),
_buildGiftSwiper(), _buildGiftSwiper(),
_buildBottomBar(), _buildBottomBar(),
], ],
@ -303,33 +309,6 @@ class _LiveGiftPopupState extends State<LiveGiftPopup> {
}); });
} }
Widget _buildTab() {
return Container(
height: 47.w,
padding: EdgeInsets.only(left: 29.w),
child: Row(
children: [
Text(
"互动",
style: TextStyle(
fontSize: 13.w,
color: const Color.fromRGBO(144, 144, 144, 1),
),
),
SizedBox(width: 40.w),
Text(
"礼物",
style: TextStyle(
fontSize: 13.w,
color: Colors.white,
fontWeight: FontWeight.w700,
),
),
],
),
);
}
Widget _buildGiftSwiper() { Widget _buildGiftSwiper() {
if (widget.giftList.isEmpty) { if (widget.giftList.isEmpty) {
return Expanded( return Expanded(
@ -370,57 +349,77 @@ class _LiveGiftPopupState extends State<LiveGiftPopup> {
} }
Widget _buildBottomBar() { Widget _buildBottomBar() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(Assets.imagesRoseGift, width: 21.w, height: 21.w),
SizedBox(width: 8.w),
Text(
"9",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
SizedBox(width: 12.w),
Image.asset(Assets.imagesRoseGift, width: 68.w, height: 33.w),
],
),
ValueListenableBuilder<int>(
valueListenable: widget.giftNum,
builder: (context, num, _) {
return Row(
children: [
GestureDetector(
onTap: () => _handleSendGift(widget.type ?? 0),
child: Container(
width: 63.w,
height: 30.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
gradient: const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromRGBO(61, 138, 224, 1),
Color.fromRGBO(131, 89, 255, 1),
],
),
),
child: Center(
child: Text(
"赠送",
style: TextStyle(fontSize: 13.w, color: Colors.white),
return SafeArea(
top: false,
child: Container(
padding: EdgeInsets.only(
left: 10.w,
right: 10.w,
bottom: MediaQuery.of(context).padding.bottom > 0
? MediaQuery.of(context).padding.bottom
: 10.h,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(Assets.imagesRoseGift, width: 21.w, height: 21.w),
SizedBox(width: 8.w),
Text(
"9",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
SizedBox(width: 12.w),
],
),
// showHeader为truefalse使targetUserId
Builder(
builder: (context) {
final bool canSend = widget.showHeader
? (_selectedUserId != null && _selectedUserId! > 0)
: (widget.targetUserId != null || _selectedUserId != null);
return Row(
children: [
GestureDetector(
onTap: canSend ? () => _handleSendGift(widget.type ?? 0) : null,
child: Opacity(
opacity: canSend ? 1.0 : 0.5,
child: Container(
width: 63.w,
height: 30.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
gradient: canSend
? const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromRGBO(61, 138, 224, 1),
Color.fromRGBO(131, 89, 255, 1),
],
)
: null,
color: canSend
? null
: Colors.grey.withOpacity(0.5),
),
child: Center(
child: Text(
"赠送",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
),
), ),
), ),
), ),
),
],
);
},
),
],
],
);
},
),
],
),
), ),
); );
} }

38
lib/widget/live/live_room_action_bar.dart

@ -79,25 +79,25 @@ class LiveRoomActionBar extends StatelessWidget {
), ),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
InkWell(
onTap: onSendTap,
child: Container(
width: 38.w,
height: 38.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(38.w)),
color: const Color.fromRGBO(0, 0, 0, .3),
),
child: Center(
child: Image.asset(
Assets.imagesArrowR,
width: 16.w,
height: 16.w,
),
),
),
),
SizedBox(width: 8.w),
// InkWell(
// onTap: onSendTap,
// child: Container(
// width: 38.w,
// height: 38.w,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.all(Radius.circular(38.w)),
// color: const Color.fromRGBO(0, 0, 0, .3),
// ),
// child: Center(
// child: Image.asset(
// Assets.imagesArrowR,
// width: 16.w,
// height: 16.w,
// ),
// ),
// ),
// ),
// SizedBox(width: 8.w),
InkWell( InkWell(
onTap: onChargeTap, onTap: onChargeTap,
child: Container( child: Container(

4
lib/widget/live/live_room_guest_list_dialog.dart

@ -626,13 +626,15 @@ class _LiveRoomGuestListDialogState extends State<LiveRoomGuestListDialog> {
// //
final channelDetail = roomController.rtcChannelDetail.value; final channelDetail = roomController.rtcChannelDetail.value;
final anchorName = channelDetail?.anchorInfo?.nickName ?? '主持人'; final anchorName = channelDetail?.anchorInfo?.nickName ?? '主持人';
final anchorAvatar = channelDetail?.anchorInfo?.profilePhoto ?? '';
// //
final messageData = <String, String>{ final messageData = <String, String>{
'type': 'live_room_invite', 'type': 'live_room_invite',
'channelId': channelId, 'channelId': channelId,
'anchorAvatar': channelDetail?.anchorInfo?.profilePhoto ?? '',
'anchorAvatar': anchorAvatar,
'anchorName': anchorName, 'anchorName': anchorName,
'coverImage': anchorAvatar, // 使
}; };
// //

83
lib/widget/message/chat_gift_popup.dart

@ -136,47 +136,56 @@ class _ChatGiftPopupState extends State<ChatGiftPopup> {
} }
Widget _buildBottomBar() { Widget _buildBottomBar() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 1
SizedBox(width: 1.w),
ValueListenableBuilder<int>(
valueListenable: widget.giftNum,
builder: (context, num, _) {
return Row(
children: [
GestureDetector(
onTap: () => _handleSendGift(),
child: Container(
width: 63.w,
height: 30.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
gradient: const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromRGBO(61, 138, 224, 1),
Color.fromRGBO(131, 89, 255, 1),
],
return SafeArea(
top: false,
child: Container(
padding: EdgeInsets.only(
left: 10.w,
right: 10.w,
bottom: MediaQuery.of(context).padding.bottom > 0
? MediaQuery.of(context).padding.bottom
: 10.h,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 1
SizedBox(width: 1.w),
ValueListenableBuilder<int>(
valueListenable: widget.giftNum,
builder: (context, num, _) {
return Row(
children: [
GestureDetector(
onTap: () => _handleSendGift(),
child: Container(
width: 63.w,
height: 30.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
gradient: const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromRGBO(61, 138, 224, 1),
Color.fromRGBO(131, 89, 255, 1),
],
),
), ),
),
child: Center(
child: Text(
"赠送",
style: TextStyle(fontSize: 13.w, color: Colors.white),
child: Center(
child: Text(
"赠送",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
), ),
), ),
), ),
),
],
);
},
),
],
],
);
},
),
],
),
), ),
); );
} }

157
lib/widget/message/room_item.dart

@ -8,6 +8,9 @@ import '../../generated/assets.dart';
import '../../controller/discover/room_controller.dart'; import '../../controller/discover/room_controller.dart';
import '../../controller/global.dart'; import '../../controller/global.dart';
import '../../controller/message/chat_controller.dart'; import '../../controller/message/chat_controller.dart';
import '../../controller/overlay_controller.dart';
import '../../rtc/rtc_manager.dart';
import '../../pages/discover/live_room_page.dart';
class RoomItem extends StatelessWidget { class RoomItem extends StatelessWidget {
final EMMessage message; final EMMessage message;
@ -32,11 +35,13 @@ class RoomItem extends StatelessWidget {
final customBody = message.body as EMCustomMessageBody; final customBody = message.body as EMCustomMessageBody;
// //
if (customBody.event == 'live_room_invite') { if (customBody.event == 'live_room_invite') {
return customBody.params;
final params = customBody.params;
print('📋 [RoomItem] 解析到的房间信息: $params');
return params;
} }
} }
} catch (e) { } catch (e) {
print('解析房间信息失败: $e');
print('❌ [RoomItem] 解析房间信息失败: $e');
} }
return null; return null;
} }
@ -56,21 +61,58 @@ class RoomItem extends StatelessWidget {
/// ///
String _getAnchorAvatar() { String _getAnchorAvatar() {
final roomInfo = _parseRoomInfo(); final roomInfo = _parseRoomInfo();
return roomInfo?['anchorAvatar'] ?? '';
final avatar = roomInfo?['anchorAvatar'] ?? '';
// URL
return avatar.trim().replaceAll('`', '');
}
/// 使
String _getCoverImage() {
final roomInfo = _parseRoomInfo();
// 使使
final coverImage = roomInfo?['anchorAvatar'] ?? '';
return coverImage.trim().replaceAll('`', '');
} }
/// ///
void _handleTap() async{
void _handleTap() async {
final channelId = _getChannelId(); final channelId = _getChannelId();
if (channelId.isNotEmpty) {
// RoomController
final roomController = Get.isRegistered<RoomController>()
? Get.find<RoomController>()
: Get.put(RoomController());
//
await roomController.joinChannel(channelId);
if (channelId.isEmpty) {
return;
}
// ID
final currentChannelId = RTCManager.instance.currentChannelId;
//
if (Get.isRegistered<OverlayController>()) {
final overlayController = Get.find<OverlayController>();
overlayController.hide();
}
// RoomController
final roomController = Get.isRegistered<RoomController>()
? Get.find<RoomController>()
: Get.put(RoomController());
// ID一致
if (currentChannelId != null && currentChannelId == channelId) {
// LiveRoomPage LiveRoomPage
final currentRoute = Get.currentRoute;
if (currentRoute != '/LiveRoomPage' && !currentRoute.contains('LiveRoomPage')) {
Get.to(() => const LiveRoomPage(id: 0));
}
return;
}
// 退
if (currentChannelId != null && currentChannelId.isNotEmpty) {
// 退
await roomController.leaveChannel();
} }
//
await roomController.joinChannel(channelId);
} }
@override @override
@ -82,6 +124,7 @@ class RoomItem extends StatelessWidget {
final anchorName = _getAnchorName(); final anchorName = _getAnchorName();
final anchorAvatar = _getAnchorAvatar(); final anchorAvatar = _getAnchorAvatar();
final coverImage = _getAnchorAvatar();
return Column( return Column(
children: [ children: [
@ -134,11 +177,15 @@ class RoomItem extends StatelessWidget {
width: 150.w, width: 150.w,
height: 150.w, height: 150.w,
color: Colors.grey[200], color: Colors.grey[200],
child: anchorAvatar.isNotEmpty
child: coverImage.isNotEmpty
? CachedNetworkImage( ? CachedNetworkImage(
imageUrl: anchorAvatar,
imageUrl: coverImage,
width: 150.w,
height: 150.w,
fit: BoxFit.cover, fit: BoxFit.cover,
placeholder: (context, url) => Container( placeholder: (context, url) => Container(
width: 150.w,
height: 150.w,
color: Colors.grey[200], color: Colors.grey[200],
child: Center( child: Center(
child: CircularProgressIndicator( child: CircularProgressIndicator(
@ -147,15 +194,19 @@ class RoomItem extends StatelessWidget {
), ),
), ),
), ),
errorWidget: (context, url, error) =>
Container(
color: Colors.grey[200],
child: Icon(
Icons.live_tv,
size: 40.w,
color: Colors.grey[400],
),
),
errorWidget: (context, url, error) {
print('❌ [RoomItem] 封面图片加载失败: $url, error: $error');
return Container(
width: 150.w,
height: 150.w,
color: Colors.grey[200],
child: Icon(
Icons.live_tv,
size: 40.w,
color: Colors.grey[400],
),
);
},
) )
: Container( : Container(
color: Colors.grey[200], color: Colors.grey[200],
@ -189,7 +240,7 @@ class RoomItem extends StatelessWidget {
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),
Text( Text(
'直播',
'直播',
style: TextStyle( style: TextStyle(
fontSize: 11.sp, fontSize: 11.sp,
color: Colors.white, color: Colors.white,
@ -214,36 +265,38 @@ class RoomItem extends StatelessWidget {
color: Colors.grey[300], color: Colors.grey[300],
child: anchorAvatar.isNotEmpty child: anchorAvatar.isNotEmpty
? CachedNetworkImage( ? CachedNetworkImage(
imageUrl: anchorAvatar,
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],
imageUrl: anchorAvatar,
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],
),
),
),
), ),
),
),
),
errorWidget: (context, url, error) =>
Icon(
Icons.person,
size: 16.w,
color: Colors.grey[600],
errorWidget: (context, url, error) =>
Image.asset(
Assets.imagesUserAvatar,
width: 24.w,
height: 24.w,
fit: BoxFit.cover,
),
)
: Image.asset(
Assets.imagesUserAvatar,
width: 24.w,
height: 24.w,
fit: BoxFit.cover,
), ),
)
: Icon(
Icons.person,
size: 16.w,
color: Colors.grey[600],
),
), ),
), ),
SizedBox(width: 5.w), SizedBox(width: 5.w),

Loading…
Cancel
Save