diff --git a/lib/widget/message/message_item.dart b/lib/widget/message/message_item.dart index 3e65e0d..008ae03 100644 --- a/lib/widget/message/message_item.dart +++ b/lib/widget/message/message_item.dart @@ -9,6 +9,7 @@ import 'voice_item.dart'; import 'video_item.dart'; import 'call_item.dart'; import 'gift_item.dart'; +import 'room_item.dart'; import '../../controller/message/chat_controller.dart'; class MessageItem extends StatelessWidget { @@ -55,6 +56,19 @@ class MessageItem extends StatelessWidget { return false; } + // 检查是否是直播间邀请消息 + bool _isRoomInviteMessage() { + try { + if (message.body.type == MessageType.CUSTOM) { + final customBody = message.body as EMCustomMessageBody; + return customBody.event == 'live_room_invite'; + } + } catch (e) { + // 解析失败,不是直播间邀请消息 + } + return false; + } + @override Widget build(BuildContext context) { print('📨 [MessageItem] 渲染消息,类型: ${message.body.type}'); @@ -96,6 +110,25 @@ class MessageItem extends StatelessWidget { }, ); } + + // 处理直播间邀请消息(自定义消息) + if (message.body.type == MessageType.CUSTOM && _isRoomInviteMessage()) { + return RoomItem( + message: message, + isSentByMe: isSentByMe, + showTime: shouldShowTime(), + formattedTime: formatMessageTime(message.serverTime), + onResend: () { + // 通过传入的 controller 或 Get 找到 ChatController 并调用重发方法 + try { + final controller = chatController ?? Get.find(); + controller.resendMessage(message); + } catch (e) { + print('重发消息失败: $e'); + } + }, + ); + } // 处理文本消息 if (message.body.type == MessageType.TXT) { diff --git a/lib/widget/message/room_item.dart b/lib/widget/message/room_item.dart new file mode 100644 index 0000000..3c52ba4 --- /dev/null +++ b/lib/widget/message/room_item.dart @@ -0,0 +1,358 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:im_flutter_sdk/im_flutter_sdk.dart'; +import 'package:get/get.dart'; + +import '../../generated/assets.dart'; +import '../../pages/discover/live_room_page.dart'; +import '../../controller/discover/room_controller.dart'; + +class RoomItem extends StatelessWidget { + final EMMessage message; + final bool isSentByMe; + final bool showTime; + final String formattedTime; + final VoidCallback? onResend; + + const RoomItem({ + required this.message, + required this.isSentByMe, + required this.showTime, + required this.formattedTime, + this.onResend, + super.key, + }); + + /// 从自定义消息中解析房间信息 + Map? _parseRoomInfo() { + try { + if (message.body.type == MessageType.CUSTOM) { + final customBody = message.body as EMCustomMessageBody; + // 检查是否是直播间邀请消息 + if (customBody.event == 'live_room_invite') { + return customBody.params; + } + } + } catch (e) { + print('解析房间信息失败: $e'); + } + return null; + } + + /// 获取频道ID + String _getChannelId() { + final roomInfo = _parseRoomInfo(); + return roomInfo?['channelId'] ?? ''; + } + + /// 获取主持人昵称 + String _getAnchorName() { + final roomInfo = _parseRoomInfo(); + return roomInfo?['anchorName'] ?? '主持人'; + } + + /// 获取主持人头像 + String _getAnchorAvatar() { + final roomInfo = _parseRoomInfo(); + return roomInfo?['anchorAvatar'] ?? ''; + } + + /// 处理点击事件 + void _handleTap() { + final channelId = _getChannelId(); + if (channelId.isNotEmpty) { + // 获取 RoomController 并加入频道 + final roomController = Get.isRegistered() + ? Get.find() + : Get.put(RoomController()); + + // 加入频道并跳转 + roomController.joinChannel(channelId).then((_) { + Get.to(() => const LiveRoomPage(id: 0)); + }).catchError((e) { + print('❌ 加入直播间失败: $e'); + }); + } + } + + @override + Widget build(BuildContext context) { + final roomInfo = _parseRoomInfo(); + if (roomInfo == null) { + return SizedBox.shrink(); + } + + final anchorName = _getAnchorName(); + final anchorAvatar = _getAnchorAvatar(); + + return Column( + children: [ + // 显示时间 + if (showTime) _buildTimeLabel(), + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), + child: Row( + mainAxisAlignment: + isSentByMe ? MainAxisAlignment.end : MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (!isSentByMe) _buildAvatar(), + if (!isSentByMe) SizedBox(width: 8.w), + // 发送消息时,状态在左侧 + if (isSentByMe) + Align( + alignment: Alignment.center, + child: Container( + margin: EdgeInsets.only(top: 10.h), + child: _buildMessageStatus(), + ), + ), + if (isSentByMe) SizedBox(width: 10.w), + // 直播间卡片 + GestureDetector( + onTap: _handleTap, + child: Container( + constraints: BoxConstraints(maxWidth: 250.w), + margin: EdgeInsets.only(top: 10.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12.w), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 8, + offset: Offset(0, 2), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(12.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 封面图片 + Stack( + children: [ + Container( + width: 250.w, + height: 250.w, + color: Colors.grey[200], + child: anchorAvatar.isNotEmpty + ? CachedNetworkImage( + imageUrl: anchorAvatar, + fit: BoxFit.cover, + placeholder: (context, url) => Container( + color: Colors.grey[200], + child: Center( + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.grey[600], + ), + ), + ), + errorWidget: (context, url, error) => + Container( + color: Colors.grey[200], + child: Icon( + Icons.live_tv, + size: 40.w, + color: Colors.grey[400], + ), + ), + ) + : Container( + color: Colors.grey[200], + child: Icon( + Icons.live_tv, + size: 40.w, + color: Colors.grey[400], + ), + ), + ), + // 直播中标签 + Positioned( + top: 8.w, + left: 8.w, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8.w, + vertical: 4.w, + ), + decoration: BoxDecoration( + color: const Color.fromRGBO(117, 98, 249, 1), + borderRadius: BorderRadius.circular(4.w), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.play_circle_filled, + size: 12.w, + color: Colors.white, + ), + SizedBox(width: 4.w), + Text( + '直播中', + style: TextStyle( + fontSize: 11.sp, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + ], + ), + // 底部信息 + Container( + padding: EdgeInsets.all(12.w), + child: Row( + children: [ + // 头像 + ClipRRect( + borderRadius: BorderRadius.circular(12.w), + child: Container( + width: 24.w, + height: 24.w, + color: Colors.grey[300], + child: anchorAvatar.isNotEmpty + ? 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], + ), + ), + ), + ), + errorWidget: (context, url, error) => + Icon( + Icons.person, + size: 16.w, + color: Colors.grey[600], + ), + ) + : Icon( + Icons.person, + size: 16.w, + color: Colors.grey[600], + ), + ), + ), + SizedBox(width: 8.w), + // 标题 + Expanded( + child: Text( + anchorName, + style: TextStyle( + fontSize: 13.sp, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + if (isSentByMe) SizedBox(width: 8.w), + if (isSentByMe) _buildAvatar(), + ], + ), + ), + ], + ); + } + + // 构建时间标签 + Widget _buildTimeLabel() { + return Container( + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 16.w), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12.w), + child: Text( + formattedTime, + style: TextStyle(fontSize: 12.sp, color: Colors.grey), + ), + ), + ); + } + + // 构建头像 + Widget _buildAvatar() { + return Container( + width: 40.w, + height: 40.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.w), + image: DecorationImage( + image: AssetImage(Assets.imagesAvatarsExample), + fit: BoxFit.cover, + ), + ), + ); + } + + // 构建消息状态(发送中、已发送、失败重发) + Widget _buildMessageStatus() { + if (!isSentByMe) { + return SizedBox.shrink(); + } + + final status = message.status; + + if (status == MessageStatus.FAIL) { + // 发送失败,显示重发按钮 + return GestureDetector( + onTap: onResend, + child: Container( + width: 20.w, + height: 20.w, + decoration: BoxDecoration( + color: Colors.red.withOpacity(0.1), + shape: BoxShape.circle, + ), + child: Icon( + Icons.refresh, + size: 14.w, + color: Colors.red, + ), + ), + ); + } else if (status == MessageStatus.PROGRESS) { + // 发送中,显示加载动画 + return Container( + width: 16.w, + height: 16.w, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Colors.grey), + ), + ); + } else { + // 发送成功,不显示任何状态 + return SizedBox.shrink(); + } + } +} +