Browse Source
feat(live): 更新直播间UI并增强用户交互功能
feat(live): 更新直播间UI并增强用户交互功能
- 新增房间用户添加图标资源 - 优化主播展示区域,支持点击添加嘉宾功能 - 改进活跃说话人组件,使用网络图片并添加加载状态 - 实现用户上麦状态判断逻辑 - 添加嘉宾列表弹窗功能 - 优化视频占位图显示逻辑 - 调整UI布局和样式细节ios
4 changed files with 252 additions and 131 deletions
Unified View
Diff Options
-
BINassets/images/room_user_add.png
-
1lib/generated/assets.dart
-
148lib/widget/live/live_room_active_speaker.dart
-
234lib/widget/live/live_room_anchor_showcase.dart
@ -1,54 +1,126 @@ |
|||||
|
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/generated/assets.dart'; |
||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
class LiveRoomActiveSpeaker extends StatelessWidget { |
class LiveRoomActiveSpeaker extends StatelessWidget { |
||||
const LiveRoomActiveSpeaker({super.key}); |
const LiveRoomActiveSpeaker({super.key}); |
||||
|
|
||||
@override |
@override |
||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.start, |
|
||||
children: [ |
|
||||
Stack( |
|
||||
clipBehavior: Clip.none, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 34.w, |
|
||||
height: 34.w, |
|
||||
margin: EdgeInsets.only(left: 13.w), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.all(Radius.circular(34.w)), |
|
||||
child: Image.asset( |
|
||||
Assets.imagesUserAvatar, |
|
||||
width: 34.w, |
|
||||
height: 34.w, |
|
||||
|
// 获取 RoomController(在 Obx 外部获取,避免重复查找) |
||||
|
final roomController = Get.isRegistered<RoomController>() |
||||
|
? Get.find<RoomController>() |
||||
|
: null; |
||||
|
|
||||
|
if (roomController == null) { |
||||
|
return const SizedBox.shrink(); |
||||
|
} |
||||
|
|
||||
|
// 获取当前用户信息 |
||||
|
final currentUserId = GlobalData().userId ?? GlobalData().userData?.id; |
||||
|
final currentUserPhoto = GlobalData().userData?.profilePhoto ?? ''; |
||||
|
|
||||
|
return Obx(() { |
||||
|
// 访问响应式变量以触发更新 |
||||
|
final rtcChannelDetail = roomController.rtcChannelDetail.value; |
||||
|
final isLive = roomController.isLive.value; |
||||
|
|
||||
|
// 判断当前用户是否上麦 |
||||
|
bool isOnSeat = false; |
||||
|
if (currentUserId != null) { |
||||
|
// 方式1:检查当前用户是否在 maleInfo 或 femaleInfo 中(maleAudience/femaleAudience 角色) |
||||
|
if (rtcChannelDetail != null) { |
||||
|
final maleInfo = rtcChannelDetail.maleInfo; |
||||
|
final femaleInfo = rtcChannelDetail.femaleInfo; |
||||
|
|
||||
|
isOnSeat = |
||||
|
(maleInfo != null && |
||||
|
(maleInfo.userId == currentUserId || |
||||
|
maleInfo.miId == currentUserId)) || |
||||
|
(femaleInfo != null && |
||||
|
(femaleInfo.userId == currentUserId || |
||||
|
femaleInfo.miId == currentUserId)); |
||||
|
} |
||||
|
|
||||
|
// 方式2:如果 isLive 为 true,说明用户已连接(可能是 audience 角色) |
||||
|
// 如果已经在 maleInfo/femaleInfo 中,就不需要再检查 isLive |
||||
|
if (!isOnSeat && isLive) { |
||||
|
isOnSeat = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 确定显示的图标 |
||||
|
final micIcon = isOnSeat ? Assets.imagesMicOpen : Assets.imagesMicClose; |
||||
|
|
||||
|
return Row( |
||||
|
mainAxisAlignment: MainAxisAlignment.start, |
||||
|
children: [ |
||||
|
Stack( |
||||
|
clipBehavior: Clip.none, |
||||
|
children: [ |
||||
|
Container( |
||||
|
width: 34.w, |
||||
|
height: 34.w, |
||||
|
margin: EdgeInsets.only(left: 13.w), |
||||
|
child: ClipRRect( |
||||
|
borderRadius: BorderRadius.all(Radius.circular(34.w)), |
||||
|
child: currentUserPhoto.isNotEmpty |
||||
|
? CachedNetworkImage( |
||||
|
imageUrl: currentUserPhoto, |
||||
|
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: 34.w, |
||||
|
height: 34.w, |
||||
|
), |
||||
|
) |
||||
|
: Image.asset( |
||||
|
Assets.imagesUserAvatar, |
||||
|
width: 34.w, |
||||
|
height: 34.w, |
||||
|
), |
||||
), |
), |
||||
), |
), |
||||
), |
|
||||
Positioned( |
|
||||
bottom: -3.w, |
|
||||
left: 20.w, |
|
||||
child: Container( |
|
||||
width: 20.w, |
|
||||
height: 20.w, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.all(Radius.circular(4.w)), |
|
||||
color: const Color.fromRGBO(0, 0, 0, .65), |
|
||||
), |
|
||||
child: Center( |
|
||||
child: Image.asset( |
|
||||
Assets.imagesMicClose, |
|
||||
width: 10.w, |
|
||||
height: 11.w, |
|
||||
|
Positioned( |
||||
|
bottom: -3.w, |
||||
|
left: 20.w, |
||||
|
child: Container( |
||||
|
width: 20.w, |
||||
|
height: 20.w, |
||||
|
decoration: BoxDecoration( |
||||
|
borderRadius: BorderRadius.all(Radius.circular(4.w)), |
||||
|
color: const Color.fromRGBO(0, 0, 0, .65), |
||||
|
), |
||||
|
child: Center( |
||||
|
child: Image.asset(micIcon, width: 10.w, height: 11.w), |
||||
), |
), |
||||
), |
), |
||||
), |
), |
||||
), |
|
||||
], |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
|
], |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
}); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
Write
Preview
Loading…
Cancel
Save