Browse Source

实现房间内邀请踢人逻辑

dev-2.0
王子贤 2 months ago
parent
commit
b6f00a78af
11 changed files with 987 additions and 527 deletions
  1. 384
      lib/controller/discover/room_controller.dart
  2. 116
      lib/model/discover/task_data.dart
  3. 3
      lib/network/api_urls.dart
  4. 6
      lib/network/user_api.dart
  5. 33
      lib/network/user_api.g.dart
  6. 81
      lib/widget/live/audience_item.dart
  7. 43
      lib/widget/live/disconnect_mic_dialog.dart
  8. 189
      lib/widget/live/kick_list.dart
  9. 313
      lib/widget/live/live_room_invitation_list.dart
  10. 181
      lib/widget/live/live_room_notice_chat_panel.dart
  11. 165
      lib/widget/live/live_room_user_header.dart

384
lib/controller/discover/room_controller.dart

@ -3,6 +3,9 @@ import 'dart:convert';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:dating_touchme_app/controller/global.dart';
import 'package:dating_touchme_app/controller/overlay_controller.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/model/discover/task_data.dart';
import 'package:dating_touchme_app/model/live/gift_product_model.dart';
import 'package:dating_touchme_app/model/rtc/link_mic_card_model.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart';
@ -14,6 +17,7 @@ import 'package:dating_touchme_app/rtc/rtm_manager.dart';
import 'package:dating_touchme_app/service/live_chat_message_service.dart';
import 'package:easy_refresh/easy_refresh.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:permission_handler/permission_handler.dart';
@ -239,6 +243,28 @@ class RoomController extends GetxController with WidgetsBindingObserver {
}
}
final taskData = TaskData().obs;
getTaskData() async {
try{
final response = await _networkService.userApi.userGetUserTaskComplete(
taskType: 1,
);
if (response.data.isSuccess && response.data.data != null) {
final data = response.data.data ?? TaskData();
taskData.value = data;
} else {
//
throw Exception(response.data.message ?? '获取数据失败');
}
} catch(e) {
print('任务列表获取失败: $e');
SmartDialog.showToast('任务列表获取失败');
rethrow;
}
}
void setDialogDismiss(bool flag){
isDialogShowing.value = flag;
}
@ -888,6 +914,269 @@ class RoomController extends GetxController with WidgetsBindingObserver {
} catch (e) {
print('❌ 处理踢人消息失败: $e');
}
} else if(message['type'] == 'invite_to_mic'){
// +
try {
final userId = message['userId'];
final operatorName = message['operatorName']?.toString() ?? '主持人';
print('✅ 收到邀请消息: 被邀请用户ID $userId');
//
if (userId.contains(GlobalData().userId ?? '')) {
if(rtcChannelDetail.value?.maleInfo == null && GlobalData().userData?.genderCode == 0 && currentRole != CurrentRole.broadcaster ||
rtcChannelDetail.value?.femaleInfo == null && GlobalData().userData?.genderCode == 1 && currentRole != CurrentRole.broadcaster ||
isLive.value && currentRole != CurrentRole.broadcaster && !isLive.value){
setDialogDismiss(true);
SmartDialog.show(
alignment: Alignment.center,
maskColor: Colors.black.withOpacity(0.5),
onDismiss: () {
setDialogDismiss(false);
},
builder: (context) {
// return LiveRoomGuestListDialog(
// initialTab: isMaleSeat ? 1 : 0, // 0: , 1:
// );
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(16.w)),
child: Material(
child: Stack(
children: [
Container(
width: 311.w,
height: 210.w,
color: Colors.white,
padding: EdgeInsets.only(
top: 53.w,
left: 23.w,
right: 23.w,
bottom: 20.w
),
child: Column(
children: [
Text(
"主持人邀请您视频连麦",
style: TextStyle(
fontSize: 21.w,
color: const Color.fromRGBO(117, 98, 249, 1),
fontWeight: FontWeight.w500
),
),
SizedBox(height: 15.w,),
if(GlobalData().userData?.genderCode == 0) Text(
"有相亲卡的用户免费",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(87, 87, 87, 1),
fontWeight: FontWeight.w500
),
),
SizedBox(height: 28.w,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 128.w,
height: 40.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12.w)),
color: const Color.fromRGBO(237, 237, 237, 1)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
Assets.imagesHangUpIcon,
width: 26.w,
height: 26.w,
),
SizedBox(width: 5.w,),
Text(
"拒绝",
style: TextStyle(
fontSize: 15.w
),
)
],
),
).onTap((){
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
setDialogDismiss(false);
}),
Container(
width: 128.w,
height: 40.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12.w)),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
Assets.imagesAnswerIcon,
width: 18.w,
height: 13.w,
),
SizedBox(width: 5.w,),
Text(
"接受",
style: TextStyle(
fontSize: 15.w,
color: Colors.white
),
)
],
),
).onTap(() async {
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
setDialogDismiss(false);
//
if (!isLive.value) {
final userData = GlobalData().userData;
final isMale = userData?.genderCode == 0;
if (isMale) {
final cardNum = linkMicCard.value?.num ?? 0;
// "上麦20玫瑰"20
if (cardNum == 0 && roseCount.value < 20) {
setDialogDismiss(true);
SmartDialog.show(
alignment: Alignment.bottomCenter,
maskColor: Colors.black.withOpacity(0.5),
onDismiss: (){
setDialogDismiss(false);
},
builder: (_) => const LiveRechargePopup(),
);
return;
}
}
}
if(isLive.value){
await leaveChat();
}else{
await joinChat(GlobalData().userData?.genderCode == 0 ? CurrentRole.maleAudience : CurrentRole.femaleAudience);
}
}),
],
)
],
),
),
Positioned(
top: 0,
left: 0,
child: Container(
padding: EdgeInsets.symmetric(vertical: 5.w, horizontal: 15.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.w),
bottomRight: Radius.circular(16.w),
),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Text(
GlobalData().userData?.genderCode == 0 ? "20玫瑰" : "免费",
style: TextStyle(
fontSize: 14.w,
color: Colors.white,
fontWeight: FontWeight.w500
),
),
),
)
],
),
),
);
},
);
}
} else {
//
final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) {
await fetchRtcChannelDetail(channelName);
}
print('✅ 其他用户收到邀请消息,已刷新房间详情');
}
} catch (e) {
print('❌ 处理邀请消息失败: $e');
}
} else if(message['type'] == 'kick_more_user'){
// +
try {
final userId = message['userId'].map((e) => int.parse(e)).toList();
final operatorName = message['operatorName']?.toString() ?? '主持人';
print('✅ 收到踢人消息: 被踢人用户ID $userId');
//
if (userId.contains(rtcChannel.value?.uid ?? '')) {
//
if (Get.isRegistered<OverlayController>()) {
try {
final overlayController = Get.find<OverlayController>();
overlayController.hide(); //
} catch (e) {
print('关闭小窗口失败: $e');
}
}
//
await leaveChannel();
//
Get.off(
() => LiveEndPage(isKickedOut: true, operatorName: operatorName),
);
} else {
//
final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) {
await fetchRtcChannelDetail(channelName);
}
print('✅ 其他用户收到踢人消息,已刷新房间详情');
}
} catch (e) {
print('❌ 处理踢人消息失败: $e');
}
} else if (message['type'] == 'end_mic') {
//
try {
final kickingUId = message['kickingUId'];
final operatorName = message['operatorName']?.toString() ?? '主持人';
print('✅ 收到下麦消息: 被下麦用户ID $kickingUId');
//
if (rtcChannel.value?.uid == kickingUId) {
await leaveChat();
} else {
//
final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) {
await fetchRtcChannelDetail(channelName);
}
print('✅ 其他用户收到下麦消息,已刷新房间详情');
}
} catch (e) {
print('❌ 处理下麦消息失败: $e');
}
}
}
@ -919,6 +1208,69 @@ class RoomController extends GetxController with WidgetsBindingObserver {
matchmakerFlag.value = GlobalData().userData!.matchmakerFlag!;
}
inviteMic(List<String> selectUserId) async {
try {
final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) {
final messageData = {
'type': 'invite_to_mic',
'userId': selectUserId,
'operatorId': GlobalData().userData?.id ?? '',
'operatorName': GlobalData().userData?.nickName ?? '',
};
await RTMManager.instance.publishChannelMessage(
channelName: channelName,
message: json.encode(messageData),
);
print('✅ 邀请消息已发送: 邀请用户ID $selectUserId');
//
await fetchRtcChannelDetail(channelName);
}
} catch (e){
print('❌ 邀请用户异常: $e');
SmartDialog.showToast('邀请用户失败');
}
}
kickUser(List<String> selectUserId) async {
try {
final futures = selectUserId.map((e){
final requestData = {'channelId': rtcChannelDetail.value!.channelId, 'kickingUId': e};
return _networkService.rtcApi.kickingRtcChannelUser(
requestData,
);
});
Future.wait(futures).then((e) async {
final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) {
final messageData = {
'type': 'kick_more_user',
'userId': selectUserId,
'operatorId': GlobalData().userData?.id ?? '',
'operatorName': GlobalData().userData?.nickName ?? '',
};
await RTMManager.instance.publishChannelMessage(
channelName: channelName,
message: json.encode(messageData),
);
print('✅ 邀请消息已发送: 邀请用户ID $selectUserId');
//
await fetchRtcChannelDetail(channelName);
}
});
} catch (e){
print('❌ 邀请用户异常: $e');
SmartDialog.showToast('邀请用户失败');
}
}
/// RTC
Future<void> kickingRtcChannelUser({
required String channelId,
@ -965,6 +1317,38 @@ class RoomController extends GetxController with WidgetsBindingObserver {
}
}
/// RTC
Future<void> endMic({
required String channelId,
required int kickingUId,
}) async {
try {
// RTM
final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) {
final messageData = {
'type': 'end_mic',
'kickingUId': kickingUId,
'operatorId': GlobalData().userData?.id ?? '',
'operatorName': GlobalData().userData?.nickName ?? '',
};
await RTMManager.instance.publishChannelMessage(
channelName: channelName,
message: json.encode(messageData),
);
print('✅ 下麦消息已发送: 下麦用户ID $kickingUId');
//
await fetchRtcChannelDetail(channelName);
}
} catch (e) {
print('❌ 下麦用户异常: $e');
SmartDialog.showToast('下麦用户失败');
}
}
///
Future<void> getUserPropLinkMicCard() async {
try {

116
lib/model/discover/task_data.dart

@ -0,0 +1,116 @@
class TaskData {
String? userTaskCompleteId;
int? taskGroup;
int? taskType;
int? stageCode;
String? taskName;
String? taskDesc;
String? taskStartDate;
String? taskEndDate;
String? rewardValue;
bool? completeStatus;
int? rewardReceiveStatus;
String? completeTime;
List<SubList>? subList;
TaskData(
{this.userTaskCompleteId,
this.taskGroup,
this.taskType,
this.stageCode,
this.taskName,
this.taskDesc,
this.taskStartDate,
this.taskEndDate,
this.rewardValue,
this.completeStatus,
this.rewardReceiveStatus,
this.completeTime,
this.subList});
TaskData.fromJson(Map<String, dynamic> json) {
userTaskCompleteId = json['userTaskCompleteId'];
taskGroup = json['taskGroup'];
taskType = json['taskType'];
stageCode = json['stageCode'];
taskName = json['taskName'];
taskDesc = json['taskDesc'];
taskStartDate = json['taskStartDate'];
taskEndDate = json['taskEndDate'];
rewardValue = json['rewardValue'];
completeStatus = json['completeStatus'];
rewardReceiveStatus = json['rewardReceiveStatus'];
completeTime = json['completeTime'];
if (json['subList'] != null) {
subList = <SubList>[];
json['subList'].forEach((v) {
subList!.add(new SubList.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['userTaskCompleteId'] = this.userTaskCompleteId;
data['taskGroup'] = this.taskGroup;
data['taskType'] = this.taskType;
data['stageCode'] = this.stageCode;
data['taskName'] = this.taskName;
data['taskDesc'] = this.taskDesc;
data['taskStartDate'] = this.taskStartDate;
data['taskEndDate'] = this.taskEndDate;
data['rewardValue'] = this.rewardValue;
data['completeStatus'] = this.completeStatus;
data['rewardReceiveStatus'] = this.rewardReceiveStatus;
data['completeTime'] = this.completeTime;
if (this.subList != null) {
data['subList'] = this.subList!.map((v) => v.toJson()).toList();
}
return data;
}
}
class SubList {
String? subTaskName;
int? subTaskType;
String? subTaskDesc;
int? requiredCount;
int? completeCount;
bool? completeStatus;
String? completeTime;
int? sort;
SubList(
{this.subTaskName,
this.subTaskType,
this.subTaskDesc,
this.requiredCount,
this.completeCount,
this.completeStatus,
this.completeTime,
this.sort});
SubList.fromJson(Map<String, dynamic> json) {
subTaskName = json['subTaskName'];
subTaskType = json['subTaskType'];
subTaskDesc = json['subTaskDesc'];
requiredCount = json['requiredCount'];
completeCount = json['completeCount'];
completeStatus = json['completeStatus'];
completeTime = json['completeTime'];
sort = json['sort'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['subTaskName'] = this.subTaskName;
data['subTaskType'] = this.subTaskType;
data['subTaskDesc'] = this.subTaskDesc;
data['requiredCount'] = this.requiredCount;
data['completeCount'] = this.completeCount;
data['completeStatus'] = this.completeStatus;
data['completeTime'] = this.completeTime;
data['sort'] = this.sort;
return data;
}
}

3
lib/network/api_urls.dart

@ -159,4 +159,7 @@ class ApiUrls {
static const String userPageMicJoinRtcChannelUser =
'dating-agency-chat-audio/user/page/mic-join/rtc-channel-user';
static const String userGetUserTaskComplete =
'dating-agency-mall/user/get/user-task-complete';
}

6
lib/network/user_api.dart

@ -1,4 +1,5 @@
import 'package:dating_touchme_app/model/common/oss_data.dart';
import 'package:dating_touchme_app/model/discover/task_data.dart';
import 'package:dating_touchme_app/model/home/user_info_data.dart';
import 'package:dating_touchme_app/model/mine/bank_card_data.dart';
import 'package:dating_touchme_app/model/mine/bank_card_ocr_data.dart';
@ -276,4 +277,9 @@ abstract class UserApi {
@Query('versionCode') required int code,
});
@GET(ApiUrls.userGetUserTaskComplete)
Future<HttpResponse<BaseResponse<TaskData>>> userGetUserTaskComplete({
@Query('taskType') required int taskType,
});
}

33
lib/network/user_api.g.dart

@ -1672,6 +1672,39 @@ class _UserApi implements UserApi {
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<TaskData>>> userGetUserTaskComplete({
required int taskType,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{r'taskType': taskType};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<HttpResponse<BaseResponse<TaskData>>>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-mall/user/get/user-task-complete',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<TaskData> _value;
try {
_value = BaseResponse<TaskData>.fromJson(
_result.data!,
(json) => TaskData.fromJson(json as Map<String, dynamic>),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
final httpResponse = HttpResponse(_value, _result);
return httpResponse;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

81
lib/widget/live/audience_item.dart

@ -0,0 +1,81 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../model/discover/audience_list_data.dart';
class AudienceItem extends StatefulWidget {
final Records item;
final List<String> selectUserId;
final Function(String) selectChange;
const AudienceItem({super.key, required this.item, required this.selectUserId, required this.selectChange});
@override
State<AudienceItem> createState() => _AudienceItemState();
}
class _AudienceItemState extends State<AudienceItem> {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(40.w)),
child: CachedNetworkImage(
imageUrl: "${widget.item.profilePhoto ?? ""}?x-oss-process=image/format,webp/resize,w_120",
width: 40.w,
height: 40.w,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
),
SizedBox(width: 5.w,),
Column(
children: [
Text(
widget.item.nickName ?? "",
style: TextStyle(
fontSize: 12.w
),
),
Text(
"${widget.item.age ?? ""}",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(121, 121, 121, 1)
),
),
],
)
],
),
Checkbox(
value: widget.selectUserId.contains(widget.item.userId),
onChanged: (value) {
widget.selectChange(widget.item.userId ?? "");
},
activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey),
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
);
}
}

43
lib/widget/live/disconnect_mic_dialog.dart

@ -141,6 +141,45 @@ class DisconnectMicDialog extends StatelessWidget {
),
SizedBox(height: 10.w),
//
GestureDetector(
onTap: isConnected
? () async {
//
await roomController.endMic(
channelId: roomController.rtcChannelDetail.value!.channelId,
kickingUId: userInfo.uid ?? 0);
SmartDialog.dismiss();
}
: null,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.w),
decoration: BoxDecoration(
color: isConnected ? const Color(0xFF8B5CF6) : Colors.grey[300],
borderRadius: BorderRadius.circular(20.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Assets.imagesMicOff,
width: 14.w,
color: isConnected ? Colors.white : Colors.grey[600],
),
SizedBox(width: 4.w),
Text(
isConnected ? '解除连麦' : '已解除连麦',
style: TextStyle(
fontSize: 12.sp,
color: isConnected ? Colors.white : Colors.grey[600],
),
),
],
),
),
),
SizedBox(height: 10.w),
//
GestureDetector(
onTap: isConnected
? () async {
@ -161,13 +200,13 @@ class DisconnectMicDialog extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Assets.imagesMicOff,
Assets.imagesGetOutIcon,
width: 14.w,
color: isConnected ? Colors.white : Colors.grey[600],
),
SizedBox(width: 4.w),
Text(
isConnected ? '解除连麦' : '解除连麦',
isConnected ? '踢出房间' : '踢出房间',
style: TextStyle(
fontSize: 12.sp,
color: isConnected ? Colors.white : Colors.grey[600],

189
lib/widget/live/kick_list.dart

@ -0,0 +1,189 @@
import 'package:dating_touchme_app/controller/discover/room_controller.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:easy_refresh/easy_refresh.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 'audience_item.dart';
class KickList extends StatefulWidget {
const KickList({super.key});
@override
State<KickList> createState() => _KickListState();
}
class _KickListState extends State<KickList> {
final RoomController _roomController = Get.find<RoomController>();
List<String> selectUserId = [];
selectChange(String userId){
if (selectUserId.contains(userId)) {
selectUserId.remove(userId);
} else {
selectUserId.add(userId);
}
setState(() {
});
}
@override
Widget build(BuildContext context) {
return Material(
borderRadius: BorderRadius.vertical(top: Radius.circular(9.w)),
color: Colors.white,
child: Container(
width: 375.w,
height: 300.w,
padding: EdgeInsets.all(10.w),
child: Column(
children: [
Text(
"在线用户",
style: TextStyle(
fontSize: 16.w,
fontWeight: FontWeight.w700
),
),
Expanded(
child: EasyRefresh(
controller: _roomController.listRefreshController,
header: const ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '刷新中...',
processingText: '刷新中...',
processedText: '刷新完成',
failedText: '刷新失败',
noMoreText: '没有更多数据',
showMessage: false
),
footer: ClassicFooter(
dragText: '上拉加载',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败',
noMoreText: '没有更多数据',
showMessage: false
),
//
onRefresh: () async {
print('推荐列表下拉刷新被触发');
_roomController.page.value = 1;
_roomController.audienceList.clear();
await _roomController.getAudienceList();
_roomController.listRefreshController.finishRefresh(IndicatorResult.success);
_roomController.listRefreshController.finishLoad(IndicatorResult.none);
},
//
onLoad: () async {
print('推荐列表上拉加载被触发, hasMore: ');
_roomController.page.value += 1;
await _roomController.getAudienceList();
},
child: ListView.separated(
// 使
// padding AppBar
padding: EdgeInsets.only(left: 12, right: 12),
itemBuilder: (context, index) {
//
if (_roomController.audienceList.isEmpty && index == 0) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('暂无数据'),
],
),
);
}
//
final item = _roomController.audienceList[index];
return AudienceItem(item: item, selectUserId: selectUserId, selectChange: selectChange,);
},
separatorBuilder: (context, index) {
//
if (_roomController.audienceList.isEmpty) {
return const SizedBox.shrink();
}
return const SizedBox(height: 12);
},
// item
itemCount: _roomController.audienceList.isEmpty ? 1 : _roomController.audienceList.length,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(237, 237, 237, 1)
),
child: Center(
child: Text(
"取消",
style: TextStyle(
fontSize: 14.w,
fontWeight: FontWeight.w500
),
),
),
).onTap((){
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
_roomController.setDialogDismiss(false);
}),
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Center(
child: Text(
"确认",
style: TextStyle(
fontSize: 14.w,
color: Colors.white,
fontWeight: FontWeight.w500
),
),
),
).onTap(() async {
final selectedA = _roomController.audienceList.where((item) => selectUserId.contains(item.userId)).toList();
final List<String> dList =
selectedA.map((item) => item.userManagementId.toString()).toList();
await _roomController.kickUser(dList);
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
_roomController.setDialogDismiss(false);
selectUserId.clear();
})
],
)
],
),
),
);
}
}

313
lib/widget/live/live_room_invitation_list.dart

@ -10,6 +10,8 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'audience_item.dart';
class LiveRoomInvitationList extends StatefulWidget {
const LiveRoomInvitationList({super.key});
@ -31,6 +33,19 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
_tabController = TabController(length: 3, vsync: this);
}
List<String> selectUserId = [];
selectChange(String userId){
if (selectUserId.contains(userId)) {
selectUserId.remove(userId);
} else {
selectUserId.add(userId);
}
setState(() {
});
}
@override
Widget build(BuildContext context) {
return Material(
@ -132,7 +147,7 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
}
//
final item = _roomController.audienceList[index];
return AudienceItem(item: item);
return AudienceItem(item: item, selectUserId: selectUserId, selectChange: selectChange,);
},
separatorBuilder: (context, index) {
//
@ -151,258 +166,68 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(237, 237, 237, 1)
),
child: Center(
child: Text(
"取消",
style: TextStyle(
fontSize: 14.w,
fontWeight: FontWeight.w500
),
),
),
),
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Center(
child: Text(
"确认",
style: TextStyle(
fontSize: 14.w,
color: Colors.white,
fontWeight: FontWeight.w500
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 15.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(237, 237, 237, 1)
),
),
).onTap(() {
_roomController.setDialogDismiss(true);
SmartDialog.show(
alignment: Alignment.center,
maskColor: Colors.black.withOpacity(0.5),
onDismiss: () {
_roomController.setDialogDismiss(false);
},
builder: (context) {
// return LiveRoomGuestListDialog(
// initialTab: isMaleSeat ? 1 : 0, // 0: , 1:
// );
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(16.w)),
child: Material(
child: Stack(
children: [
Container(
width: 311.w,
height: 210.w,
color: Colors.white,
padding: EdgeInsets.only(
top: 53.w,
left: 23.w,
right: 23.w,
bottom: 20.w
),
child: Column(
children: [
Text(
"主持人邀请您视频连麦",
style: TextStyle(
fontSize: 21.w,
color: const Color.fromRGBO(117, 98, 249, 1),
fontWeight: FontWeight.w500
),
),
SizedBox(height: 15.w,),
Text(
"有相亲卡的用户免费",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(87, 87, 87, 1),
fontWeight: FontWeight.w500
),
),
SizedBox(height: 28.w,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 128.w,
height: 40.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12.w)),
color: const Color.fromRGBO(237, 237, 237, 1)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
Assets.imagesHangUpIcon,
width: 26.w,
height: 26.w,
),
SizedBox(width: 5.w,),
Text(
"拒绝",
style: TextStyle(
fontSize: 15.w
),
)
],
),
),
Container(
width: 128.w,
height: 40.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12.w)),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
Assets.imagesAnswerIcon,
width: 18.w,
height: 13.w,
),
SizedBox(width: 5.w,),
Text(
"拒绝",
style: TextStyle(
fontSize: 15.w,
color: Colors.white
),
)
],
),
),
],
)
],
),
),
Positioned(
top: 0,
left: 0,
child: Container(
padding: EdgeInsets.symmetric(vertical: 5.w, horizontal: 15.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.w),
bottomRight: Radius.circular(16.w),
),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Text(
"20玫瑰",
style: TextStyle(
fontSize: 14.w,
color: Colors.white,
fontWeight: FontWeight.w500
),
),
),
)
],
),
child: Center(
child: Text(
"取消",
style: TextStyle(
fontSize: 14.w,
fontWeight: FontWeight.w500
),
);
},
);
})
],
)
],
),
),
);
}
}
class AudienceItem extends StatefulWidget {
final Records item;
const AudienceItem({super.key, required this.item});
@override
State<AudienceItem> createState() => _AudienceItemState();
}
class _AudienceItemState extends State<AudienceItem> {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(40.w)),
child: CachedNetworkImage(
imageUrl: "${widget.item.profilePhoto ?? ""}?x-oss-process=image/format,webp/resize,w_120",
width: 40.w,
height: 40.w,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
),
SizedBox(width: 5.w,),
Column(
children: [
Text(
widget.item.nickName ?? "",
style: TextStyle(
fontSize: 12.w
).onTap((){
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
_roomController.setDialogDismiss(false);
}),
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(117, 98, 249, 1)
),
),
Text(
"${widget.item.age ?? ""}",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(121, 121, 121, 1)
child: Center(
child: Text(
"确认",
style: TextStyle(
fontSize: 14.w,
color: Colors.white,
fontWeight: FontWeight.w500
),
),
),
),
).onTap(() async {
await _roomController.inviteMic(selectUserId);
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
_roomController.setDialogDismiss(false);
selectUserId.clear();
})
],
)
],
),
Checkbox(
value: false,
onChanged: (value) {
},
activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey),
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
)
],
),
),
);
}
}

181
lib/widget/live/live_room_notice_chat_panel.dart

@ -3,6 +3,7 @@ import 'package:dating_touchme_app/controller/global.dart';
import 'package:dating_touchme_app/controller/overlay_controller.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/model/discover/task_data.dart';
import 'package:dating_touchme_app/pages/discover/task_detail.dart';
import 'package:dating_touchme_app/widget/live/live_room_chat_item.dart';
import 'package:dating_touchme_app/widget/live/live_recharge_popup.dart';
@ -216,13 +217,14 @@ class _LiveRoomNoticeChatPanelState extends State<LiveRoomNoticeChatPanel> {
if(isHost) Image.asset(
Assets.imagesDailyTasks,
width: 70.w,
).onTap((){
).onTap(() async {
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
roomController.setDialogDismiss(true);
await roomController.getTaskData();
SmartDialog.show(
alignment: Alignment.bottomCenter,
@ -304,126 +306,9 @@ class _LiveRoomNoticeChatPanelState extends State<LiveRoomNoticeChatPanel> {
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Column(
children: [
Container(
width: 350.w,
margin: EdgeInsets.only(bottom: 10.w),
padding: EdgeInsets.only(
top: 15.w,
right: 10.w,
bottom: 21.w,
left: 12.w
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"开播4小时",
style: TextStyle(
fontSize: 14.w
),
),
Text(
"50/240分钟",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(144, 144, 144, 1)
),
)
],
),
SizedBox(height: 10.w,),
TDProgress(
type: TDProgressType.linear,
value: 0.5,
strokeWidth: 6,
progressLabelPosition: TDProgressLabelPosition.inside,
showLabel: false,
)
],
),
),
Container(
width: 350.w,
margin: EdgeInsets.only(bottom: 10.w),
padding: EdgeInsets.only(
top: 15.w,
right: 10.w,
bottom: 21.w,
left: 12.w
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"开播4小时",
style: TextStyle(
fontSize: 14.w
),
),
Text(
"50/240分钟",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(144, 144, 144, 1)
),
)
],
),
SizedBox(height: 10.w,),
TDProgress(
type: TDProgressType.linear,
value: 0.5,
strokeWidth: 6,
progressLabelPosition: TDProgressLabelPosition.inside,
showLabel: false,
)
],
),
),
Container(
width: 350.w,
margin: EdgeInsets.only(bottom: 10.w),
padding: EdgeInsets.only(
top: 15.w,
right: 10.w,
bottom: 21.w,
left: 12.w
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"开播4小时",
style: TextStyle(
fontSize: 14.w
),
),
Text(
"50/240分钟",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(144, 144, 144, 1)
),
)
],
),
SizedBox(height: 10.w,),
TDProgress(
type: TDProgressType.linear,
value: 0.5,
strokeWidth: 6,
progressLabelPosition: TDProgressLabelPosition.inside,
showLabel: false,
)
],
),
),
...roomController.taskData.value.subList!.map((e){
return TaskItem(item: e,);
})
],
),
),
@ -443,3 +328,57 @@ class _LiveRoomNoticeChatPanelState extends State<LiveRoomNoticeChatPanel> {
);
}
}
class TaskItem extends StatefulWidget {
final SubList item;
const TaskItem({super.key, required this.item});
@override
State<TaskItem> createState() => _TaskItemState();
}
class _TaskItemState extends State<TaskItem> {
@override
Widget build(BuildContext context) {
return Container(
width: 350.w,
margin: EdgeInsets.only(bottom: 10.w),
padding: EdgeInsets.only(
top: 15.w,
right: 10.w,
bottom: 21.w,
left: 12.w
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.item.subTaskDesc ?? "",
style: TextStyle(
fontSize: 14.w
),
),
Text(
"${widget.item.completeCount ?? ""}/${widget.item.requiredCount ?? ""}${widget.item.sort == 1 ? "分钟" : widget.item.sort == 2 ? "" : ""}",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(144, 144, 144, 1)
),
)
],
),
SizedBox(height: 10.w,),
TDProgress(
type: TDProgressType.linear,
value: ((widget.item.completeCount ?? 0) / (widget.item.requiredCount ?? 0)),
strokeWidth: 6,
progressLabelPosition: TDProgressLabelPosition.inside,
showLabel: false,
)
],
),
);
}
}

165
lib/widget/live/live_room_user_header.dart

@ -2,6 +2,8 @@ import 'package:dating_touchme_app/controller/discover/room_controller.dart';
import 'package:dating_touchme_app/controller/overlay_controller.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/widget/live/disconnect_mic_dialog.dart';
import 'package:dating_touchme_app/widget/live/kick_list.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@ -114,6 +116,8 @@ class LiveRoomUserHeader extends StatelessWidget {
// "解除连麦"
GestureDetector(
onTap: () async {
roomController.audienceList.clear();
await roomController.getAudienceList();
//
FocusScope.of(context).unfocus();
// overlay
@ -129,166 +133,7 @@ class LiveRoomUserHeader extends StatelessWidget {
},
builder: (_) {
return Material(
borderRadius: BorderRadius.vertical(top: Radius.circular(9.w)),
color: Colors.white,
child: Container(
width: 375.w,
height: 300.w,
padding: EdgeInsets.all(10.w),
child: Column(
children: [
Text(
"在麦用户",
style: TextStyle(
fontSize: 16.w,
fontWeight: FontWeight.w700
),
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
Assets.imagesUserAvatar,
width: 40.w,
height: 40.w,
),
SizedBox(width: 5.w,),
Column(
children: [
Text(
"开心",
style: TextStyle(
fontSize: 12.w
),
),
Text(
"22岁",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(121, 121, 121, 1)
),
),
],
)
],
),
Checkbox(
value: false,
onChanged: (value) {
},
activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey),
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
Assets.imagesUserAvatar,
width: 40.w,
height: 40.w,
),
SizedBox(width: 5.w,),
Column(
children: [
Text(
"开心",
style: TextStyle(
fontSize: 12.w
),
),
Text(
"22岁",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(121, 121, 121, 1)
),
),
],
)
],
),
Checkbox(
value: false,
onChanged: (value) {
},
activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey),
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
),
],
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(237, 237, 237, 1)
),
child: Center(
child: Text(
"取消",
style: TextStyle(
fontSize: 14.w,
fontWeight: FontWeight.w500
),
),
),
),
Container(
width: 170.w,
height: 42.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.w)),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Center(
child: Text(
"确认",
style: TextStyle(
fontSize: 14.w,
color: Colors.white,
fontWeight: FontWeight.w500
),
),
),
)
],
)
],
),
),
);
return KickList();
},
);
},

Loading…
Cancel
Save