diff --git a/lib/pages/discover/live_room_page.dart b/lib/pages/discover/live_room_page.dart index 001faff..29c566c 100644 --- a/lib/pages/discover/live_room_page.dart +++ b/lib/pages/discover/live_room_page.dart @@ -30,14 +30,6 @@ class _LiveRoomPageState extends State { final activeGift = ValueNotifier(null); - List giftList = [ - {"icon": Assets.imagesGift1, "title": "爱心礼物", "price": 30}, - {"icon": Assets.imagesGift2, "title": "小小小星星", "price": 30}, - {"icon": Assets.imagesGift3, "title": "助威", "price": 30}, - {"icon": Assets.imagesGift4, "title": "点赞", "price": 30}, - {"icon": Assets.imagesGift5, "title": "崇拜衣柜", "price": 30}, - ]; - final giftNum = ValueNotifier(1); final activePay = ValueNotifier(null); @@ -99,12 +91,18 @@ class _LiveRoomPageState extends State { SmartDialog.show( alignment: Alignment.bottomCenter, maskColor: TDTheme.of(context).fontGyColor2, - builder: (_) => LiveGiftPopup( - activeGift: activeGift, - giftNum: giftNum, - giftList: giftList, - changeActive: changeActive, - ), + builder: (_) => Obx(() { + // 优先使用 API 返回的 giftProducts,如果为空则使用后备列表 + final giftProducts = _roomController.giftProducts; + final giftList = giftProducts.toList(); + + return LiveGiftPopup( + activeGift: activeGift, + giftNum: giftNum, + giftList: giftList, + changeActive: changeActive, + ); + }), ); } diff --git a/lib/widget/live/live_gift_popup.dart b/lib/widget/live/live_gift_popup.dart index c0376dc..0da1d71 100644 --- a/lib/widget/live/live_gift_popup.dart +++ b/lib/widget/live/live_gift_popup.dart @@ -1,11 +1,16 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dating_touchme_app/controller/discover/room_controller.dart'; +import 'package:dating_touchme_app/controller/global.dart'; import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart'; import 'package:dating_touchme_app/widget/live/live_room_gift_item.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; +import 'package:get/get.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; -class LiveGiftPopup extends StatelessWidget { +class LiveGiftPopup extends StatefulWidget { const LiveGiftPopup({ super.key, required this.activeGift, @@ -16,9 +21,44 @@ class LiveGiftPopup extends StatelessWidget { final ValueNotifier activeGift; final ValueNotifier giftNum; - final List giftList; + final List giftList; // 支持 List 或 List final void Function(int) changeActive; + @override + State createState() => _LiveGiftPopupState(); +} + +class _LiveGiftPopupState extends State { + // 选中的用户ID集合 + final Set _selectedUserIds = {}; + + // 切换用户选中状态 + void _toggleUserSelection(String userId) { + setState(() { + if (_selectedUserIds.contains(userId)) { + _selectedUserIds.remove(userId); + } else { + _selectedUserIds.add(userId); + } + }); + } + + // 全选/取消全选 + void _toggleSelectAll(List users) { + setState(() { + if (_selectedUserIds.length == users.length) { + // 如果已全选,则取消全选 + _selectedUserIds.clear(); + } else { + // 否则全选 + _selectedUserIds.clear(); + for (var user in users) { + _selectedUserIds.add(user.userId); + } + } + }); + } + @override Widget build(BuildContext context) { return Material( @@ -32,7 +72,9 @@ class LiveGiftPopup extends StatelessWidget { Expanded( child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.vertical(top: Radius.circular(9.w)), + borderRadius: BorderRadius.vertical( + top: Radius.circular(9.w), + ), color: const Color.fromRGBO(22, 19, 28, 1), ), child: Column( @@ -51,88 +93,166 @@ class LiveGiftPopup extends StatelessWidget { } Widget _buildHeader() { - return Container( - height: 53.w, - padding: EdgeInsets.symmetric(horizontal: 10.w), - child: Row( - children: [ - Row( - children: [ - Text( - "送给: ", - style: TextStyle(fontSize: 13.w, color: Colors.white), - ), - SizedBox(width: 6.w), - ...List.generate(3, (index) { - return Padding( - padding: EdgeInsets.only(right: 10.w), - child: Stack( - children: [ - ClipRRect( - borderRadius: BorderRadius.all( - Radius.circular(index == 0 ? 68.w : 34.w), - ), - child: Container( - width: 34.w, - height: 34.w, - decoration: BoxDecoration( + return Obx(() { + // 获取 RoomController + final roomController = Get.isRegistered() + ? Get.find() + : null; + + // 获取 rtcChannelDetail + final rtcChannelDetail = roomController?.rtcChannelDetail.value; + + // 获取当前用户ID + final currentUserId = GlobalData().userId ?? GlobalData().userData?.id; + + // 构建用户列表(anchorInfo, maleInfo, femaleInfo) + final List userList = []; + if (rtcChannelDetail?.anchorInfo != null) { + userList.add(rtcChannelDetail!.anchorInfo!); + } + if (rtcChannelDetail?.maleInfo != null) { + userList.add(rtcChannelDetail!.maleInfo!); + } + if (rtcChannelDetail?.femaleInfo != null) { + userList.add(rtcChannelDetail!.femaleInfo!); + } + + // 移除本人 + final filteredUserList = userList.where((user) { + // 通过 userId 或 miId 判断是否是本人 + return user.userId != currentUserId && user.miId != currentUserId; + }).toList(); + + // 最多显示3个用户 + final displayUsers = filteredUserList.take(3).toList(); + + return Container( + height: 53.w, + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Row( + children: [ + Row( + children: [ + Text( + "送给: ", + style: TextStyle(fontSize: 13.w, color: Colors.white), + ), + SizedBox(width: 6.w), + ...displayUsers.asMap().entries.map((entry) { + final index = entry.key; + final user = entry.value; + final isSelected = _selectedUserIds.contains(user.userId); + return GestureDetector( + onTap: () => _toggleUserSelection(user.userId), + child: Padding( + padding: EdgeInsets.only(right: 10.w), + child: Stack( + children: [ + ClipRRect( borderRadius: BorderRadius.all( Radius.circular(index == 0 ? 68.w : 34.w), ), - border: Border.all( - width: index == 0 ? 2 : 1, - color: const Color.fromRGBO(117, 98, 249, 1), + child: Container( + width: 34.w, + height: 34.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(index == 0 ? 68.w : 34.w), + ), + border: Border.all( + width: index == 0 ? 2 : 1, + color: const Color.fromRGBO(117, 98, 249, 1), + ), + ), + child: user.profilePhoto.isNotEmpty + ? CachedNetworkImage( + imageUrl: user.profilePhoto, + width: 34.w, + height: 34.w, + fit: BoxFit.cover, + placeholder: (context, url) => Container( + width: 34.w, + height: 34.w, + color: Colors.grey[300], + child: Center( + child: SizedBox( + width: 16.w, + height: 16.w, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.grey[600], + ), + ), + ), + ), + errorWidget: (context, url, error) => + Image.asset( + Assets.imagesUserAvatar, + width: 32.w, + height: 32.w, + ), + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 32.w, + height: 32.w, + ), ), ), - child: Image.asset( - Assets.imagesUserAvatar, - width: 32.w, - height: 32.w, - ), - ), - ), - Positioned( - bottom: 0, - right: 2.w, - child: Container( - width: 12.w, - height: 12.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(12.w)), - color: const Color.fromRGBO(117, 98, 249, 1), - ), - child: Center( - child: Image.asset( - Assets.imagesCheck, - width: 6.w, - height: 4.w, + if (isSelected) + Positioned( + bottom: 0, + right: 2.w, + child: Container( + width: 12.w, + height: 12.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(12.w), + ), + color: const Color.fromRGBO(117, 98, 249, 1), + ), + child: Center( + child: Image.asset( + Assets.imagesCheck, + width: 6.w, + height: 4.w, + ), + ), + ), ), - ), - ), + ], ), - ], - ), - ); - }), - ], - ), - Container( - width: 63.w, - height: 30.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30.w)), - color: const Color.fromRGBO(117, 98, 249, 1), + ), + ); + }), + ], ), - child: Center( - child: Text( - "全选", - style: TextStyle(fontSize: 13.w, color: Colors.white), + // 如果列表为空,不显示全选按钮 + if (displayUsers.isNotEmpty) + GestureDetector( + onTap: () => _toggleSelectAll(displayUsers), + child: Container( + width: 63.w, + height: 30.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(30.w)), + color: const Color.fromRGBO(117, 98, 249, 1), + ), + child: Center( + child: Text( + _selectedUserIds.length == displayUsers.length + ? "取消全选" + : "全选", + style: TextStyle(fontSize: 13.w, color: Colors.white), + ), + ), + ), ), - ), - ), - ], - ), - ); + ], + ), + ); + }); } Widget _buildTab() { @@ -163,34 +283,61 @@ class LiveGiftPopup extends StatelessWidget { } Widget _buildGiftSwiper() { + if (widget.giftList.isEmpty) { + return Expanded( + child: Center( + child: Text( + '暂无礼物', + style: TextStyle(fontSize: 14.w, color: Colors.white70), + ), + ), + ); + } + + // 计算需要多少页,每页显示8个礼物(2行4列) + final itemsPerPage = 8; + final totalPages = (widget.giftList.length / itemsPerPage).ceil(); + return Expanded( child: ValueListenableBuilder( - valueListenable: activeGift, + valueListenable: widget.activeGift, builder: (context, active, _) { return Swiper( autoplay: false, - itemCount: 6, - loop: true, - pagination: const SwiperPagination( - alignment: Alignment.bottomCenter, - builder: TDSwiperDotsPagination( - color: Color.fromRGBO(144, 144, 144, 1), - activeColor: Color.fromRGBO(77, 77, 77, 1), - ), - ), - itemBuilder: (context, index) { + itemCount: totalPages, + loop: false, + pagination: totalPages > 1 + ? const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperDotsPagination( + color: Color.fromRGBO(144, 144, 144, 1), + activeColor: Color.fromRGBO(77, 77, 77, 1), + ), + ) + : null, + itemBuilder: (context, pageIndex) { + final startIndex = pageIndex * itemsPerPage; + final endIndex = (startIndex + itemsPerPage).clamp( + 0, + widget.giftList.length, + ); + final pageItems = widget.giftList.sublist(startIndex, endIndex); + return Align( alignment: Alignment.topCenter, child: Wrap( + spacing: 7.w, + runSpacing: 7.w, children: [ - ...giftList.asMap().entries.map( - (entry) => LiveRoomGiftItem( + ...pageItems.asMap().entries.map((entry) { + final globalIndex = startIndex + entry.key; + return LiveRoomGiftItem( item: entry.value, active: active ?? 0, - index: entry.key, - changeActive: changeActive, - ), - ), + index: globalIndex, + changeActive: widget.changeActive, + ); + }), ], ), ); @@ -209,26 +356,18 @@ class LiveGiftPopup extends StatelessWidget { children: [ Row( children: [ - Image.asset( - Assets.imagesRoseGift, - width: 21.w, - height: 21.w, - ), + 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, - ), + Image.asset(Assets.imagesRoseGift, width: 68.w, height: 33.w), ], ), ValueListenableBuilder( - valueListenable: giftNum, + valueListenable: widget.giftNum, builder: (context, num, _) { return Row( children: [ @@ -236,8 +375,8 @@ class LiveGiftPopup extends StatelessWidget { label: "-", enabled: num > 1, onTap: () { - if (giftNum.value <= 1) return; - giftNum.value -= 1; + if (widget.giftNum.value <= 1) return; + widget.giftNum.value -= 1; }, ), SizedBox( @@ -253,7 +392,7 @@ class LiveGiftPopup extends StatelessWidget { label: "+", enabled: true, onTap: () { - giftNum.value += 1; + widget.giftNum.value += 1; }, ), SizedBox(width: 9.w), @@ -323,4 +462,3 @@ class LiveGiftPopup extends StatelessWidget { ); } } - diff --git a/lib/widget/live/live_room_gift_item.dart b/lib/widget/live/live_room_gift_item.dart index a74be2e..d1987c7 100644 --- a/lib/widget/live/live_room_gift_item.dart +++ b/lib/widget/live/live_room_gift_item.dart @@ -1,8 +1,10 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dating_touchme_app/model/live/gift_product_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class LiveRoomGiftItem extends StatefulWidget { - final Map item; + final dynamic item; // 支持 Map 或 GiftProductModel final int active; final int index; final void Function(int) changeActive; @@ -19,6 +21,83 @@ class LiveRoomGiftItem extends StatefulWidget { } class _LiveRoomGiftItemState extends State { + // 判断是否为 GiftProductModel + bool get _isGiftProductModel => widget.item is GiftProductModel; + + // 获取图片 + Widget _buildImage() { + if (_isGiftProductModel) { + final gift = widget.item as GiftProductModel; + if (gift.mainPic.isNotEmpty) { + return CachedNetworkImage( + imageUrl: gift.mainPic, + width: 41.w, + height: 41.w, + fit: BoxFit.cover, + placeholder: (context, url) => Container( + width: 41.w, + height: 41.w, + color: Colors.grey[300], + child: Center( + child: SizedBox( + width: 20.w, + height: 20.w, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.grey[600], + ), + ), + ), + ), + errorWidget: (context, url, error) => Container( + width: 41.w, + height: 41.w, + color: Colors.grey[300], + child: Icon(Icons.error_outline, size: 20.w, color: Colors.grey), + ), + ); + } else { + return Container( + width: 41.w, + height: 41.w, + color: Colors.grey[300], + ); + } + } else { + final map = widget.item as Map; + final icon = map["icon"] as String?; + if (icon != null && icon.isNotEmpty) { + return Image.asset(icon, width: 41.w, height: 41.w); + } else { + return Container( + width: 41.w, + height: 41.w, + color: Colors.grey[300], + ); + } + } + } + + // 获取标题 + String _getTitle() { + if (_isGiftProductModel) { + return (widget.item as GiftProductModel).productTitle; + } else { + return (widget.item as Map)["title"]?.toString() ?? ''; + } + } + + // 获取价格 + String _getPrice() { + if (_isGiftProductModel) { + final price = (widget.item as GiftProductModel).unitSellingPrice; + return "${price.toInt()}支"; + } else { + final price = (widget.item as Map)["price"]; + return "${price ?? 0}支"; + } + } + @override Widget build(BuildContext context) { return InkWell( @@ -49,15 +128,17 @@ class _LiveRoomGiftItemState extends State { ), child: Column( children: [ - Image.asset(widget.item["icon"], width: 41.w, height: 41.w), + _buildImage(), SizedBox(height: 7.w), Text( - widget.item["title"], + _getTitle(), style: TextStyle(fontSize: 11.w, color: Colors.white), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), SizedBox(height: 1.w), Text( - "${widget.item["price"]}支", + _getPrice(), style: TextStyle( fontSize: 7.w, color: const Color.fromRGBO(144, 144, 144, 1),