From a7c406124e645224ba85d7816918c35ce0885acd Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Sun, 28 Dec 2025 21:48:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(live):=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7ID=E6=94=AF=E6=8C=81=E5=B9=B6=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E6=B6=88=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 LiveChatMessage 模型中添加 uid 字段支持 - 更新消息序列化逻辑以包含用户ID信息 - 重构用户资料对话框为独立组件并优化显示逻辑 - 改进聊天消息滚动和自动定位到底部的机制 - 优化礼物赠送消息的发送和显示逻辑 - 移除过时的用户信息查找代码并提升性能 - 调整直播间页面布局结构和组件组织方式 --- lib/controller/discover/room_controller.dart | 118 ++++++---- lib/model/live/live_chat_message.dart | 13 +- lib/pages/discover/live_room_page.dart | 69 +++--- lib/service/live_chat_message_service.dart | 4 +- lib/widget/live/live_room_chat_item.dart | 208 +----------------- .../live/live_room_notice_chat_panel.dart | 23 +- .../live/live_room_user_profile_dialog.dart | 195 ++++++++++++++++ 7 files changed, 336 insertions(+), 294 deletions(-) create mode 100644 lib/widget/live/live_room_user_profile_dialog.dart diff --git a/lib/controller/discover/room_controller.dart b/lib/controller/discover/room_controller.dart index a6663c2..69a20b6 100644 --- a/lib/controller/discover/room_controller.dart +++ b/lib/controller/discover/room_controller.dart @@ -429,14 +429,50 @@ class RoomController extends GetxController with WidgetsBindingObserver { ); print('✅ 礼物已添加到播放队列: ${gift.productTitle}'); - // 发送 RTM 消息通知其他用户 + // 在公屏显示赠送礼物消息 + final senderNickName = GlobalData().userData?.nickName ?? '用户'; + String targetNickName = '用户'; + + // 从频道详情中查找目标用户昵称 + final channelDetail = rtcChannelDetail.value; + if (channelDetail != null) { + // 检查是否是主持人 + final anchorInfo = channelDetail.anchorInfo; + if (anchorInfo != null && anchorInfo.uid == targetUserId) { + targetNickName = anchorInfo.nickName.isNotEmpty + ? anchorInfo.nickName + : '用户'; + } + // 检查是否是男嘉宾 + else { + final maleInfo = channelDetail.maleInfo; + if (maleInfo != null && maleInfo.uid == targetUserId) { + targetNickName = maleInfo.nickName.isNotEmpty + ? maleInfo.nickName + : '用户'; + } + // 检查是否是女嘉宾 + else { + final femaleInfo = channelDetail.femaleInfo; + if (femaleInfo != null && femaleInfo.uid == targetUserId) { + targetNickName = femaleInfo.nickName.isNotEmpty + ? femaleInfo.nickName + : '用户'; + } + } + } + } + + // 发送 RTM 消息通知其他用户(包含目标用户昵称) final messageData = { 'type': 'gift', 'svgaFile': gift.svgaFile, 'giftProductId': gift.productId, 'targetUserId': targetUserId, + 'targetNickName': targetNickName, // 包含目标用户昵称 'senderUserId': rtcChannel.value?.uid, - 'senderNickName': GlobalData().userData?.nickName ?? '', + 'senderNickName': senderNickName, + 'senderAvatar': GlobalData().userData?.profilePhoto ?? '', }; await RTMManager.instance.publishChannelMessage( @@ -445,27 +481,6 @@ class RoomController extends GetxController with WidgetsBindingObserver { ); print('✅ 礼物消息已发送: ${gift.productTitle}'); - // 在公屏显示赠送礼物消息 - final senderNickName = GlobalData().userData?.nickName ?? '用户'; - String targetNickName = '用户'; - - // 从频道详情中查找目标用户昵称 - final channelDetail = rtcChannelDetail.value; - if (channelDetail != null) { - // 检查是否是主持人 - if (channelDetail.anchorInfo?.uid == targetUserId) { - targetNickName = channelDetail.anchorInfo?.nickName ?? '用户'; - } - // 检查是否是男嘉宾 - else if (channelDetail.maleInfo?.uid == targetUserId) { - targetNickName = channelDetail.maleInfo?.nickName ?? '用户'; - } - // 检查是否是女嘉宾 - else if (channelDetail.femaleInfo?.uid == targetUserId) { - targetNickName = channelDetail.femaleInfo?.nickName ?? '用户'; - } - } - // 创建公屏消息 final giftMessage = LiveChatMessage( userId: GlobalData().userId ?? GlobalData().userData?.id ?? '', @@ -491,8 +506,10 @@ class RoomController extends GetxController with WidgetsBindingObserver { final svgaFile = message['svgaFile']?.toString() ?? ''; final giftProductId = message['giftProductId']?.toString(); final targetUserId = message['targetUserId']; + final targetNickNameFromMessage = message['targetNickName']?.toString(); final senderUserId = message['senderUserId']; final senderNickName = message['senderNickName']?.toString() ?? '用户'; + final senderAvatar = message['senderAvatar']?.toString(); // 从礼物产品列表中查找礼物名称 String giftTitle = '礼物'; @@ -508,21 +525,44 @@ class RoomController extends GetxController with WidgetsBindingObserver { } } - // 获取目标用户昵称 - String targetNickName = '用户'; - final channelDetail = rtcChannelDetail.value; - if (channelDetail != null) { - // 检查是否是主持人 - if (channelDetail.anchorInfo?.uid == targetUserId) { - targetNickName = channelDetail.anchorInfo?.nickName ?? '用户'; - } - // 检查是否是男嘉宾 - else if (channelDetail.maleInfo?.userId == targetUserId) { - targetNickName = channelDetail.maleInfo?.nickName ?? '用户'; - } - // 检查是否是女嘉宾 - else if (channelDetail.femaleInfo?.userId == targetUserId) { - targetNickName = channelDetail.femaleInfo?.nickName ?? '用户'; + // 获取目标用户昵称(优先使用消息中的昵称,如果不存在则从频道详情中查找) + String targetNickName = targetNickNameFromMessage ?? '用户'; + if (targetNickName == '用户' || targetNickName.isEmpty) { + final channelDetail = rtcChannelDetail.value; + if (channelDetail != null) { + final targetUserIdStr = targetUserId?.toString() ?? ''; + final targetUid = targetUserId is int + ? targetUserId + : (int.tryParse(targetUserIdStr) ?? 0); + + // 检查是否是主持人(同时检查 uid 和 userId) + final anchorInfo = channelDetail.anchorInfo; + if (anchorInfo != null && + (anchorInfo.uid == targetUid || anchorInfo.userId == targetUserIdStr)) { + targetNickName = anchorInfo.nickName.isNotEmpty + ? anchorInfo.nickName + : '用户'; + } + // 检查是否是男嘉宾(同时检查 uid 和 userId) + else { + final maleInfo = channelDetail.maleInfo; + if (maleInfo != null && + (maleInfo.uid == targetUid || maleInfo.userId == targetUserIdStr)) { + targetNickName = maleInfo.nickName.isNotEmpty + ? maleInfo.nickName + : '用户'; + } + // 检查是否是女嘉宾(同时检查 uid 和 userId) + else { + final femaleInfo = channelDetail.femaleInfo; + if (femaleInfo != null && + (femaleInfo.uid == targetUid || femaleInfo.userId == targetUserIdStr)) { + targetNickName = femaleInfo.nickName.isNotEmpty + ? femaleInfo.nickName + : '用户'; + } + } + } } } @@ -530,7 +570,7 @@ class RoomController extends GetxController with WidgetsBindingObserver { final giftMessage = LiveChatMessage( userId: senderUserId?.toString() ?? '', userName: senderNickName, - avatar: null, // 接收到的消息可能没有头像信息 + avatar: senderAvatar?.isNotEmpty == true ? senderAvatar : null, content: '向$targetNickName赠送了【$giftTitle】', timestamp: DateTime.now().millisecondsSinceEpoch, ); diff --git a/lib/model/live/live_chat_message.dart b/lib/model/live/live_chat_message.dart index 0d87a93..e755350 100644 --- a/lib/model/live/live_chat_message.dart +++ b/lib/model/live/live_chat_message.dart @@ -1,6 +1,7 @@ /// 直播间聊天消息模型 class LiveChatMessage { final String userId; + final int? uid; final String userName; final String? avatar; final String content; @@ -10,17 +11,19 @@ class LiveChatMessage { required this.userId, required this.userName, this.avatar, + this.uid, required this.content, required this.timestamp, }); factory LiveChatMessage.fromJson(Map json) { return LiveChatMessage( - userId: json['userId'] ?? json['uid'] ?? '', - userName: json['userName'] ?? json['nickName'] ?? '用户', - avatar: json['avatar'] ?? json['profilePhoto'], - content: json['content'] ?? json['message'] ?? '', - timestamp: json['timestamp'] ?? DateTime.now().millisecondsSinceEpoch, + userId: json['userId'], + userName: json['userName'], + avatar: json['avatar'], + uid: json['uid'], + content: json['content'], + timestamp: json['timestamp'], ); } diff --git a/lib/pages/discover/live_room_page.dart b/lib/pages/discover/live_room_page.dart index 1611e89..a39219e 100644 --- a/lib/pages/discover/live_room_page.dart +++ b/lib/pages/discover/live_room_page.dart @@ -274,48 +274,37 @@ class _LiveRoomPageState extends State { padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), child: Column( children: [ - Expanded( - child: SingleChildScrollView( - child: Column( - children: [ - SizedBox(height: 10.w), - Obx(() { - final detail = - _roomController.rtcChannelDetail.value; - final anchorInfo = detail?.anchorInfo; + SizedBox(height: 10.w), + Obx(() { + final detail = _roomController.rtcChannelDetail.value; + final anchorInfo = detail?.anchorInfo; - final userName = anchorInfo!.nickName; - final avatarAsset = anchorInfo.profilePhoto; - const popularityText = '0'; // TODO: 使用真实数据 + final userName = anchorInfo!.nickName; + final avatarAsset = anchorInfo.profilePhoto; + 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(); - } - _overlayController.show(); - Get.back(); - }, - ); - }), - SizedBox(height: 7.w), - LiveRoomAnchorShowcase(), - SizedBox(height: 5.w), - const LiveRoomActiveSpeaker(), - SizedBox(height: 9.w), - const LiveRoomNoticeChatPanel(), - SizedBox(height: 10.w), - ], - ), - ), - ), + return LiveRoomUserHeader( + userName: userName, + popularityText: popularityText, + avatarAsset: avatarAsset, + onCloseTap: () { + SmartDialog.dismiss(); + // 退出房间时清空RTM消息 + if (Get.isRegistered()) { + final roomController = Get.find(); + roomController.chatMessages.clear(); + } + _overlayController.show(); + Get.back(); + }, + ); + }), + 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( diff --git a/lib/service/live_chat_message_service.dart b/lib/service/live_chat_message_service.dart index 4dedf1f..e433d38 100644 --- a/lib/service/live_chat_message_service.dart +++ b/lib/service/live_chat_message_service.dart @@ -132,11 +132,13 @@ class LiveChatMessageService { final userData = GlobalData().userData; final userName = userData?.nickName ?? '用户'; final avatar = userData?.profilePhoto; - + RoomController controller = Get.find(); + final uid = controller.rtcChannel.value?.uid; return { 'type': 'chat_message', 'userId': userId, 'userName': userName, + 'uid': uid, 'avatar': avatar, 'content': content.trim(), 'timestamp': DateTime.now().millisecondsSinceEpoch, diff --git a/lib/widget/live/live_room_chat_item.dart b/lib/widget/live/live_room_chat_item.dart index a5247b4..ef1f8cd 100644 --- a/lib/widget/live/live_room_chat_item.dart +++ b/lib/widget/live/live_room_chat_item.dart @@ -3,14 +3,13 @@ import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/model/live/live_chat_message.dart'; import 'package:dating_touchme_app/widget/live/live_gift_popup.dart'; +import 'package:dating_touchme_app/widget/live/live_room_user_profile_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import '../../pages/message/chat_page.dart'; - class LiveRoomChatItem extends StatelessWidget { const LiveRoomChatItem({super.key, required this.message}); @@ -20,22 +19,7 @@ class LiveRoomChatItem extends StatelessWidget { final roomController = Get.find(); // 尝试从频道详情中根据 userId 找到对应的用户信息 - final channelDetail = roomController.rtcChannelDetail.value; - int? targetUid; - - // 尝试匹配用户 - if (channelDetail?.anchorInfo?.userId == message.userId) { - targetUid = channelDetail?.anchorInfo?.uid; - } else if (channelDetail?.maleInfo?.userId == message.userId) { - targetUid = channelDetail?.maleInfo?.uid; - } else if (channelDetail?.femaleInfo?.userId == message.userId) { - targetUid = channelDetail?.femaleInfo?.uid; - } - - if (targetUid == null) { - SmartDialog.showToast('无法获取用户信息'); - return; - } + int? targetUid = message.uid; // 创建必要的 ValueNotifier final activeGift = ValueNotifier(null); @@ -66,188 +50,6 @@ class LiveRoomChatItem extends StatelessWidget { ); } - void _showUserProfileDialog(BuildContext context) { - SmartDialog.show( - alignment: Alignment.bottomCenter, - builder: (context) { - return Stack( - children: [ - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20.w), - topRight: Radius.circular(20.w), - ), - ), - height: 200.w, - margin: EdgeInsets.only(top: 40.w), - padding: EdgeInsets.symmetric(horizontal: 15.w), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox(height: 10.w), - // 用户头像和信息 - Row( - children: [ - SizedBox(width: 110.w), - // 用户名和标签 - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - message.userName, - style: TextStyle( - fontSize: 18.w, - fontWeight: FontWeight.w600, - color: const Color.fromRGBO(51, 51, 51, 1), - ), - ), - SizedBox(height: 8.w), - // 标签 - Wrap( - spacing: 6.w, - runSpacing: 6.w, - children: [ - _buildTag( - '在线', - const Color.fromRGBO(198, 246, 213, 1), - ), - ], - ), - ], - ), - ), - GestureDetector( - onTap: () => SmartDialog.dismiss(), - child: Icon( - Icons.close, - size: 24.w, - color: const Color.fromRGBO(153, 153, 153, 1), - ), - ), - ], - ), - SizedBox(height: 25.w), - // 送礼物按钮 - GestureDetector( - onTap: () { - SmartDialog.dismiss(); - _showGiftPopup(context, 1); - }, - child: Container( - width: double.infinity, - height: 44.w, - decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [ - Color.fromRGBO(117, 98, 249, 1), - Color.fromRGBO(152, 124, 255, 1), - ], - begin: Alignment.centerLeft, - end: Alignment.centerRight, - ), - borderRadius: BorderRadius.circular(22.w), - ), - child: Center( - child: Text( - '送礼物', - style: TextStyle( - fontSize: 16.w, - color: Colors.white, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ), - SizedBox(height: 15.w), - // 底部操作链接 - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildActionLink('私聊', () { - SmartDialog.dismiss(); - Get.to(() => ChatPage( - userId: message.userId, - )); - }), - Container( - width: 1.w, - height: 12.w, - color: const Color.fromRGBO(229, 229, 229, 1), - margin: EdgeInsets.symmetric(horizontal: 15.w), - ), - _buildActionLink('送礼物加好友', () { - SmartDialog.dismiss(); - _showGiftPopup(context, 1); - }), - ], - ), - ], - ), - ), - Container( - margin: EdgeInsets.only(left: 30.w), - child: ClipOval( - child: message.avatar != null && message.avatar!.isNotEmpty - ? Image.network( - message.avatar!, - width: 80.w, - height: 80.w, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return Image.asset( - Assets.imagesUserAvatar, - width: 60.w, - height: 60.w, - ); - }, - ) - : Image.asset( - Assets.imagesUserAvatar, - width: 60.w, - height: 60.w, - ), - ), - ), - ], - ); - }, - ); - } - - Widget _buildTag(String text, Color bgColor) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 3.w), - decoration: BoxDecoration( - color: bgColor, - borderRadius: BorderRadius.circular(10.w), - ), - child: Text( - text, - style: TextStyle( - fontSize: 11.w, - color: const Color.fromRGBO(51, 51, 51, 1), - ), - ), - ); - } - - Widget _buildActionLink(String text, VoidCallback onTap) { - return GestureDetector( - onTap: onTap, - child: Text( - text, - style: TextStyle( - fontSize: 12.w, - color: const Color.fromRGBO(153, 153, 153, 1), - ), - ), - ); - } - @override Widget build(BuildContext context) { return Container( @@ -275,7 +77,11 @@ class LiveRoomChatItem extends StatelessWidget { height: 25.w, ), ).onTap(() { - _showUserProfileDialog(context); + showUserProfileDialog( + context, + message, + () => _showGiftPopup(context, 1), + ); }) : Image.asset( Assets.imagesUserAvatar, diff --git a/lib/widget/live/live_room_notice_chat_panel.dart b/lib/widget/live/live_room_notice_chat_panel.dart index 9a278bd..588698b 100644 --- a/lib/widget/live/live_room_notice_chat_panel.dart +++ b/lib/widget/live/live_room_notice_chat_panel.dart @@ -78,7 +78,6 @@ class _LiveRoomNoticeChatPanelState extends State { @override Widget build(BuildContext context) { return Container( - height: 230.w, padding: EdgeInsets.only(left: 13.w, right: 9.w), child: Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -86,13 +85,21 @@ class _LiveRoomNoticeChatPanelState extends State { Expanded( child: Obx(() { // 监听消息列表变化,自动滚动到底部 + final messages = controller.chatMessages; + + // 使用 WidgetsBinding 确保在下一帧执行滚动 WidgetsBinding.instance.addPostFrameCallback((_) { - if (scrollController.hasClients) { - scrollController.animateTo( - scrollController.position.maxScrollExtent, - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, - ); + if (scrollController.hasClients && messages.isNotEmpty) { + // 延迟一点时间确保内容已渲染 + Future.microtask(() { + if (scrollController.hasClients) { + scrollController.animateTo( + scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + ); + } + }); } }); @@ -110,7 +117,7 @@ class _LiveRoomNoticeChatPanelState extends State { ), SizedBox(height: 15.w), // 显示聊天消息列表 - ...controller.chatMessages.map( + ...messages.map( (message) => LiveRoomChatItem(message: message), ), ], diff --git a/lib/widget/live/live_room_user_profile_dialog.dart b/lib/widget/live/live_room_user_profile_dialog.dart new file mode 100644 index 0000000..f72d5b8 --- /dev/null +++ b/lib/widget/live/live_room_user_profile_dialog.dart @@ -0,0 +1,195 @@ +import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/model/live/live_chat_message.dart'; +import 'package:dating_touchme_app/pages/message/chat_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; + +/// 显示用户资料对话框 +void showUserProfileDialog( + BuildContext context, + LiveChatMessage message, + VoidCallback onShowGiftPopup, +) { + SmartDialog.show( + alignment: Alignment.bottomCenter, + builder: (context) { + return Stack( + children: [ + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.w), + topRight: Radius.circular(20.w), + ), + ), + height: 200.w, + margin: EdgeInsets.only(top: 40.w), + padding: EdgeInsets.symmetric(horizontal: 15.w), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: 10.w), + // 用户头像和信息 + Row( + children: [ + SizedBox(width: 110.w), + // 用户名和标签 + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + message.userName, + style: TextStyle( + fontSize: 18.w, + fontWeight: FontWeight.w600, + color: const Color.fromRGBO(51, 51, 51, 1), + ), + ), + SizedBox(height: 8.w), + // 标签 + Wrap( + spacing: 6.w, + runSpacing: 6.w, + children: [ + _buildTag( + '在线', + const Color.fromRGBO(198, 246, 213, 1), + ), + ], + ), + ], + ), + ), + GestureDetector( + onTap: () => SmartDialog.dismiss(), + child: Icon( + Icons.close, + size: 24.w, + color: const Color.fromRGBO(153, 153, 153, 1), + ), + ), + ], + ), + SizedBox(height: 25.w), + // 送礼物按钮 + GestureDetector( + onTap: () { + SmartDialog.dismiss(); + onShowGiftPopup(); + }, + child: Container( + width: double.infinity, + height: 44.w, + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [ + Color.fromRGBO(117, 98, 249, 1), + Color.fromRGBO(152, 124, 255, 1), + ], + begin: Alignment.centerLeft, + end: Alignment.centerRight, + ), + borderRadius: BorderRadius.circular(22.w), + ), + child: Center( + child: Text( + '送礼物', + style: TextStyle( + fontSize: 16.w, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ), + SizedBox(height: 15.w), + // 底部操作链接 + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildActionLink('私聊', () { + SmartDialog.dismiss(); + Get.to(() => ChatPage( + userId: message.userId, + )); + }), + Container( + width: 1.w, + height: 12.w, + color: const Color.fromRGBO(229, 229, 229, 1), + margin: EdgeInsets.symmetric(horizontal: 15.w), + ), + _buildActionLink('送礼物加好友', () { + SmartDialog.dismiss(); + onShowGiftPopup(); + }), + ], + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(left: 30.w), + child: ClipOval( + child: message.avatar != null && message.avatar!.isNotEmpty + ? Image.network( + message.avatar!, + width: 80.w, + height: 80.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 60.w, + height: 60.w, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 60.w, + height: 60.w, + ), + ), + ), + ], + ); + }, + ); +} + +Widget _buildTag(String text, Color bgColor) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 3.w), + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(10.w), + ), + child: Text( + text, + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(51, 51, 51, 1), + ), + ), + ); +} + +Widget _buildActionLink(String text, VoidCallback onTap) { + return GestureDetector( + onTap: onTap, + child: Text( + text, + style: TextStyle( + fontSize: 12.w, + color: const Color.fromRGBO(153, 153, 153, 1), + ), + ), + ); +} +