Browse Source

对接接口,完善功能,增加麦克风控制,实现直播间外观众邀请

dev-2.0
王子贤 2 months ago
parent
commit
0da00c4d8c
18 changed files with 524 additions and 98 deletions
  1. 129
      lib/controller/discover/room_controller.dart
  2. 95
      lib/im/im_manager.dart
  3. 15
      lib/model/discover/live_abstract_data.dart
  4. 7
      lib/model/mine/chat_static_data.dart
  5. 4
      lib/model/rtc/rtc_channel_detail.dart
  6. 3
      lib/network/api_urls.dart
  7. 11
      lib/network/rtc_api.dart
  8. 35
      lib/network/rtc_api.g.dart
  9. 39
      lib/pages/discover/settlement_page.dart
  10. 3
      lib/pages/mine/my_wallet_page.dart
  11. 3
      lib/rtc/rtc_manager.dart
  12. 8
      lib/widget/live/audience_item.dart
  13. 16
      lib/widget/live/kick_list.dart
  14. 29
      lib/widget/live/live_room_anchor_showcase.dart
  15. 5
      lib/widget/live/live_room_chat_item.dart
  16. 77
      lib/widget/live/live_room_invitation_list.dart
  17. 141
      lib/widget/live/live_room_user_header.dart
  18. 2
      lib/widget/live/live_room_user_profile_dialog.dart

129
lib/controller/discover/room_controller.dart

@ -5,8 +5,10 @@ import 'package:dating_touchme_app/controller/global.dart';
import 'package:dating_touchme_app/controller/overlay_controller.dart'; import 'package:dating_touchme_app/controller/overlay_controller.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/im/im_manager.dart';
import 'package:dating_touchme_app/model/discover/task_data.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/live/gift_product_model.dart';
import 'package:dating_touchme_app/model/mine/chat_static_data.dart';
import 'package:dating_touchme_app/model/rtc/link_mic_card_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'; import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart'; import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart';
@ -108,6 +110,49 @@ class RoomController extends GetxController with WidgetsBindingObserver {
chatMessages.clear(); chatMessages.clear();
} }
final durationMins = 0.obs;
getDurationMins() async {
try {
final channelId = RTCManager.instance.currentChannelId;
final response = await _networkService.rtcApi.userGetRtcChannelAbstract(channelId: channelId ?? "");
if (response.data.isSuccess && response.data.data != null) {
durationMins.value = response.data.data?.durationMins ?? 0;
} else {
//
throw Exception(response.data.message ?? '获取数据失败');
}
} catch(e){
print('钱包数据获取失败: $e');
SmartDialog.showToast('钱包数据获取失败');
rethrow;
}
}
final consumption = ChatStaticData(todayLiveDurationMins: 0).obs;
getLiveData() async {
try {
final channelId = RTCManager.instance.currentChannelId;
final response = await _networkService.userApi.getChatStaticsInfo();
if (response.data.isSuccess && response.data.data != null) {
consumption.value = response.data.data!;
} else {
//
throw Exception(response.data.message ?? '获取数据失败');
}
} catch(e){
print('钱包数据获取失败: $e');
SmartDialog.showToast('钱包数据获取失败');
rethrow;
}
}
final tab = 0.obs; final tab = 0.obs;
changeTab(int i) async { changeTab(int i) async {
@ -126,6 +171,7 @@ class RoomController extends GetxController with WidgetsBindingObserver {
audienceList.clear(); audienceList.clear();
await getFriendList(); await getFriendList();
} }
update();
listRefreshController.finishRefresh(IndicatorResult.success); listRefreshController.finishRefresh(IndicatorResult.success);
listRefreshController.finishLoad(IndicatorResult.none); listRefreshController.finishLoad(IndicatorResult.none);
} }
@ -155,6 +201,7 @@ class RoomController extends GetxController with WidgetsBindingObserver {
} else { } else {
listRefreshController.finishLoad(IndicatorResult.noMore); listRefreshController.finishLoad(IndicatorResult.noMore);
} }
update();
} else { } else {
// //
@ -186,6 +233,7 @@ class RoomController extends GetxController with WidgetsBindingObserver {
} else { } else {
listRefreshController.finishLoad(IndicatorResult.noMore); listRefreshController.finishLoad(IndicatorResult.noMore);
} }
update();
} else { } else {
// //
@ -232,6 +280,7 @@ class RoomController extends GetxController with WidgetsBindingObserver {
} else { } else {
listRefreshController.finishLoad(IndicatorResult.noMore); listRefreshController.finishLoad(IndicatorResult.noMore);
} }
update();
} else { } else {
// //
@ -1211,13 +1260,37 @@ class RoomController extends GetxController with WidgetsBindingObserver {
} }
} }
inviteMic(List<String> selectUserId) async {
int calculateAge(String birthdayStr) {
final birthday = DateTime.parse(birthdayStr); // 1996-1-20
final today = DateTime.now();
int age = today.year - birthday.year;
// 1
if (today.month < birthday.month ||
(today.month == birthday.month && today.day < birthday.day)) {
age--;
}
return age;
}
inviteMic(List<Map<String, dynamic>> selectUserId) async {
try { try {
final channelName = RTCManager.instance.currentChannelId; final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) { if (channelName != null && channelName.isNotEmpty) {
final idIsNull = selectUserId
.where((e) => e["uid"] == 0)
.toList();
final idNotNull = selectUserId
.where((e) => e["uid"] != 0)
.toList();
final userIds = idNotNull.map((e) => e["userId"]).toList();
final messageData = { final messageData = {
'type': 'invite_to_mic', 'type': 'invite_to_mic',
'userId': selectUserId,
'userId': userIds,
'operatorId': GlobalData().userData?.id ?? '', 'operatorId': GlobalData().userData?.id ?? '',
'operatorName': GlobalData().userData?.nickName ?? '', 'operatorName': GlobalData().userData?.nickName ?? '',
}; };
@ -1226,6 +1299,58 @@ class RoomController extends GetxController with WidgetsBindingObserver {
channelName: channelName, channelName: channelName,
message: json.encode(messageData), message: json.encode(messageData),
); );
final others = idIsNull.map((e) => e["userId"]).toList();
final channelId = RTCManager.instance.currentChannelId;
if (channelId == null || channelId.isEmpty) {
SmartDialog.showToast('频道ID不存在');
return;
}
//
final channelDetail = rtcChannelDetail.value;
final anchorName = channelDetail?.anchorInfo?.nickName ?? '主持人';
final anchorAvatar = channelDetail?.anchorInfo?.profilePhoto ?? '';
// URL不为空且格式正确
final cleanedAvatar = anchorAvatar.trim().replaceAll('`', '');
final cleanedCover = cleanedAvatar; // 使
// type event 'live_room_invite'
final messageParams = <String, String>{
'channelId': channelId,
'anchorAvatar': cleanedAvatar,
'anchorName': anchorName,
'coverImage': cleanedCover, // 使
'genderCode': GlobalData().userData?.genderCode.toString() ?? "",
"age": calculateAge(
(GlobalData().userData?.birthDate != null && GlobalData().userData!.birthDate!.isNotEmpty) ?
(GlobalData().userData?.birthDate ?? "") : GlobalData().userData?.birthYear != null && GlobalData().userData!.birthYear!.isNotEmpty ?
"${GlobalData().userData?.birthYear}-01-01" : "").toString(),
"provinceName": GlobalData().userData?.provinceName ?? "",
};
print('📤 [LiveRoomGuestListDialog] 发送房间邀请消息: anchorAvatar=$cleanedAvatar, coverImage=$cleanedCover');
others.forEach((e) async {
//
final result = await IMManager.instance.sendCustomMessage(
e,
'live_room_invite',
messageParams,
);
if (result != null) {
SmartDialog.showToast('邀请已发送');
SmartDialog.dismiss();
} else {
SmartDialog.showToast('邀请发送失败,id$e');
}
});
print('✅ 邀请消息已发送: 邀请用户ID $selectUserId'); print('✅ 邀请消息已发送: 邀请用户ID $selectUserId');
// //

95
lib/im/im_manager.dart

@ -1,5 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'dart:async'; import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/rtc/rtm_manager.dart'; import 'package:dating_touchme_app/rtc/rtm_manager.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -11,13 +13,17 @@ import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import 'package:video_thumbnail/video_thumbnail.dart'; import 'package:video_thumbnail/video_thumbnail.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import '../controller/discover/room_controller.dart';
import '../controller/message/conversation_controller.dart'; import '../controller/message/conversation_controller.dart';
import '../controller/message/chat_controller.dart'; import '../controller/message/chat_controller.dart';
import '../controller/global.dart'; import '../controller/global.dart';
import '../controller/overlay_controller.dart';
import '../generated/assets.dart'; import '../generated/assets.dart';
import '../pages/discover/live_room_page.dart';
import '../pages/mine/login_page.dart'; import '../pages/mine/login_page.dart';
import '../pages/message/chat_page.dart'; import '../pages/message/chat_page.dart';
import '../network/user_api.dart'; import '../network/user_api.dart';
import '../rtc/rtc_manager.dart';
import '../widget/message/message_notification_dialog.dart'; import '../widget/message/message_notification_dialog.dart';
import '../widget/message/video_call_invite_dialog.dart'; import '../widget/message/video_call_invite_dialog.dart';
import '../pages/message/video_call_page.dart'; import '../pages/message/video_call_page.dart';
@ -234,7 +240,6 @@ class IMManager {
if(message.body.type == MessageType.CUSTOM){ if(message.body.type == MessageType.CUSTOM){
final body = message.body as EMCustomMessageBody; final body = message.body as EMCustomMessageBody;
if(body.event == "live_room_invite"){ if(body.event == "live_room_invite"){
print(23232323232);
SmartDialog.show( SmartDialog.show(
alignment: Alignment.center, alignment: Alignment.center,
maskColor: Colors.black.withOpacity(0.5), maskColor: Colors.black.withOpacity(0.5),
@ -270,10 +275,32 @@ class IMManager {
Positioned( Positioned(
left: 38.5.w, left: 38.5.w,
top: 0, top: 0,
child: Image.asset(
Assets.imagesUserAvatar,
width: 100.w,
height: 100.w,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(100.w)),
child: CachedNetworkImage(
imageUrl: "${ body.params?["coverImage"] ?? ""}?x-oss-process=image/format,webp/resize,w_240",
width: 100.w,
height: 100.w,
fit: BoxFit.cover,
placeholder: (context, url) => Container(
color: Colors.white38,
child: Center(
child: CircularProgressIndicator(
strokeWidth: 1.w,
color: Colors.grey,
),
),
),
errorWidget: (context, url, error) =>
Image.asset(
Assets.imagesUserAvatar,
width: 100.w,
height: 100.w,
fit: BoxFit.cover,
),
),
), ),
), ),
Positioned( Positioned(
@ -288,7 +315,7 @@ class IMManager {
), ),
SizedBox(height: 10.w,), SizedBox(height: 10.w,),
Text( Text(
"开心的橘子",
body.params?["anchorName"] ?? "",
style: TextStyle( style: TextStyle(
fontSize: 18.w, fontSize: 18.w,
), ),
@ -297,7 +324,7 @@ class IMManager {
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
if(false) Container(
if(body.params?["genderCode"] == "1") Container(
width: 33.w, width: 33.w,
height: 13.w, height: 13.w,
margin: EdgeInsets.only(right: 6.w), margin: EdgeInsets.only(right: 6.w),
@ -315,7 +342,7 @@ class IMManager {
), ),
SizedBox(width: 2.w,), SizedBox(width: 2.w,),
Text( Text(
"19",
body.params?["age"] ?? "",
style: TextStyle( style: TextStyle(
fontSize: 11.w, fontSize: 11.w,
color: const Color.fromRGBO(255, 66, 236, 1) color: const Color.fromRGBO(255, 66, 236, 1)
@ -324,7 +351,7 @@ class IMManager {
], ],
), ),
), ),
Container(
if(body.params?["genderCode"] == "0") Container(
width: 33.w, width: 33.w,
height: 13.w, height: 13.w,
margin: EdgeInsets.only(right: 6.w), margin: EdgeInsets.only(right: 6.w),
@ -342,7 +369,7 @@ class IMManager {
), ),
SizedBox(width: 2.w,), SizedBox(width: 2.w,),
Text( Text(
"19",
body.params?["age"] ?? "",
style: TextStyle( style: TextStyle(
fontSize: 11.w, fontSize: 11.w,
color: const Color.fromRGBO(120, 140, 255, 1) color: const Color.fromRGBO(120, 140, 255, 1)
@ -358,7 +385,7 @@ class IMManager {
color: const Color.fromRGBO(245, 247, 255, 1) color: const Color.fromRGBO(245, 247, 255, 1)
), ),
child: Text( child: Text(
"北京",
body.params?["provinceName"] ?? "",
style: TextStyle( style: TextStyle(
fontSize: 12.w, fontSize: 12.w,
fontWeight: FontWeight.w400 fontWeight: FontWeight.w400
@ -408,7 +435,12 @@ class IMManager {
) )
], ],
), ),
),
).onTap(() {
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
}),
Container( Container(
width: 128.w, width: 128.w,
height: 40.w, height: 40.w,
@ -434,7 +466,44 @@ class IMManager {
) )
], ],
), ),
),
).onTap(() async {
//
FocusScope.of(context).unfocus();
// overlay
SmartDialog.dismiss();
// ID
final currentChannelId = RTCManager.instance.currentChannelId;
//
if (Get.isRegistered<OverlayController>()) {
final overlayController = Get.find<OverlayController>();
overlayController.hide();
}
// RoomController
final roomController = Get.isRegistered<RoomController>()
? Get.find<RoomController>()
: Get.put(RoomController());
// ID一致
if (currentChannelId != null && currentChannelId == body.params?["channelId"]) {
// LiveRoomPage LiveRoomPage
final currentRoute = Get.currentRoute;
if (currentRoute != '/LiveRoomPage' && !currentRoute.contains('LiveRoomPage')) {
Get.to(() => const LiveRoomPage(id: 0));
}
return;
}
// 退
if (currentChannelId != null && currentChannelId.isNotEmpty) {
// 退
await roomController.leaveChannel();
}
//
await roomController.joinChannel(body.params?["channelId"] ?? "");
}),
], ],
) )
], ],

15
lib/model/discover/live_abstract_data.dart

@ -0,0 +1,15 @@
class LiveAbstractData {
int? durationMins;
LiveAbstractData({this.durationMins});
LiveAbstractData.fromJson(Map<String, dynamic> json) {
durationMins = json['durationMins'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['durationMins'] = this.durationMins;
return data;
}
}

7
lib/model/mine/chat_static_data.dart

@ -4,8 +4,9 @@ class ChatStaticData {
int? liveDurationMins; int? liveDurationMins;
int? liveConsumptionAmount; int? liveConsumptionAmount;
int? todayCrossMicCount; int? todayCrossMicCount;
int? todayLiveDurationMins;
ChatStaticData({this.id, this.liveDurationMins, this.todayCrossMicCount, this.liveConsumptionAmount});
ChatStaticData({this.id, this.liveDurationMins, this.todayCrossMicCount, this.liveConsumptionAmount, this.todayLiveDurationMins});
// JSON映射创建实例 // JSON映射创建实例
factory ChatStaticData.fromJson(Map<String, dynamic> json) { factory ChatStaticData.fromJson(Map<String, dynamic> json) {
@ -14,6 +15,7 @@ class ChatStaticData {
liveDurationMins: json['liveDurationMins'] ?? 0, liveDurationMins: json['liveDurationMins'] ?? 0,
liveConsumptionAmount: json['liveConsumptionAmount'] ?? 0, liveConsumptionAmount: json['liveConsumptionAmount'] ?? 0,
todayCrossMicCount: json['todayCrossMicCount'] ?? 0, todayCrossMicCount: json['todayCrossMicCount'] ?? 0,
todayLiveDurationMins: json['todayLiveDurationMins'] ?? 0,
); );
} }
@ -24,12 +26,13 @@ class ChatStaticData {
'liveDurationMins': liveDurationMins, 'liveDurationMins': liveDurationMins,
'todayCrossMicCount': todayCrossMicCount, 'todayCrossMicCount': todayCrossMicCount,
'liveConsumptionAmount': liveConsumptionAmount, 'liveConsumptionAmount': liveConsumptionAmount,
'todayLiveDurationMins': todayLiveDurationMins,
}; };
} }
@override @override
String toString() { String toString() {
return 'LoginData(id: $id, liveDurationMins: $liveDurationMins, liveConsumptionAmount: $liveConsumptionAmount)';
return 'LoginData(id: $id, liveDurationMins: $liveDurationMins, liveConsumptionAmount: $liveConsumptionAmount, todayLiveDurationMins: $todayLiveDurationMins)';
} }
} }

4
lib/model/rtc/rtc_channel_detail.dart

@ -45,11 +45,11 @@ class RtcSeatUserInfo {
final int genderCode; final int genderCode;
final int seatNumber; final int seatNumber;
final bool isFriend; final bool isFriend;
final bool isMicrophoneOn;
bool isMicrophoneOn;
final bool isVideoOn; final bool isVideoOn;
final int? uid; final int? uid;
const RtcSeatUserInfo({
RtcSeatUserInfo({
required this.miId, required this.miId,
required this.userId, required this.userId,
required this.nickName, required this.nickName,

3
lib/network/api_urls.dart

@ -169,4 +169,7 @@ class ApiUrls {
static const String listMatchmakerTask = static const String listMatchmakerTask =
'dating-agency-mall/user/get/user-task-complete'; 'dating-agency-mall/user/get/user-task-complete';
static const String userGetRtcChannelAbstract =
'dating-agency-chat-audio/user/get/rtc-channel/abstract';
} }

11
lib/network/rtc_api.dart

@ -11,6 +11,8 @@ import 'package:dating_touchme_app/network/response_model.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart'; import 'package:retrofit/retrofit.dart';
import '../model/discover/live_abstract_data.dart';
part 'rtc_api.g.dart'; part 'rtc_api.g.dart';
/// RTC /// RTC
@ -158,4 +160,13 @@ abstract class RtcApi {
@Query('channelId') required String channelId, @Query('channelId') required String channelId,
} }
); );
///
@GET(ApiUrls.userGetRtcChannelAbstract)
Future<HttpResponse<BaseResponse<LiveAbstractData>>> userGetRtcChannelAbstract(
{
@Query('channelId') required String channelId,
}
);
} }

35
lib/network/rtc_api.g.dart

@ -872,6 +872,41 @@ class _RtcApi implements RtcApi {
return httpResponse; return httpResponse;
} }
@override
Future<HttpResponse<BaseResponse<LiveAbstractData>>>
userGetRtcChannelAbstract({required String channelId}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{r'channelId': channelId};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options =
_setStreamType<HttpResponse<BaseResponse<LiveAbstractData>>>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/get/rtc-channel/abstract',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl),
),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<LiveAbstractData> _value;
try {
_value = BaseResponse<LiveAbstractData>.fromJson(
_result.data!,
(json) => LiveAbstractData.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) { RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic && if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes || !(requestOptions.responseType == ResponseType.bytes ||

39
lib/pages/discover/settlement_page.dart

@ -1,3 +1,4 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/components/page_appbar.dart'; import 'package:dating_touchme_app/components/page_appbar.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/generated/assets.dart';
@ -9,6 +10,7 @@ import 'package:get/get.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../controller/discover/room_controller.dart'; import '../../controller/discover/room_controller.dart';
import '../../controller/global.dart';
class SettlementPage extends StatefulWidget { class SettlementPage extends StatefulWidget {
const SettlementPage({super.key}); const SettlementPage({super.key});
@ -46,7 +48,7 @@ class _SettlementPageState extends State<SettlementPage> {
child: Column( child: Column(
children: [ children: [
Text( Text(
"开心的橘子",
GlobalData().userData!.nickName ?? "",
style: TextStyle( style: TextStyle(
fontSize: 14.w, fontSize: 14.w,
color: Colors.white color: Colors.white
@ -83,7 +85,7 @@ class _SettlementPageState extends State<SettlementPage> {
), ),
children: [ children: [
TextSpan( TextSpan(
text: "0.35",
text: ((_roomController.consumption.value.todayLiveDurationMins ?? 0) / 60).toStringAsFixed(2),
style: TextStyle( style: TextStyle(
fontSize: 23.w, fontSize: 23.w,
) )
@ -117,7 +119,7 @@ class _SettlementPageState extends State<SettlementPage> {
), ),
children: [ children: [
TextSpan( TextSpan(
text: "0.35",
text: ((_roomController.consumption.value.todayLiveDurationMins ?? 0) / 60).toStringAsFixed(2),
style: TextStyle( style: TextStyle(
fontSize: 23.w, fontSize: 23.w,
) )
@ -132,7 +134,7 @@ class _SettlementPageState extends State<SettlementPage> {
), ),
), ),
Text( Text(
"今日开播",
"今日有效开播",
style: TextStyle( style: TextStyle(
fontSize: 12.w, fontSize: 12.w,
color: const Color.fromRGBO(117, 98, 249, 1) color: const Color.fromRGBO(117, 98, 249, 1)
@ -151,13 +153,13 @@ class _SettlementPageState extends State<SettlementPage> {
), ),
children: [ children: [
TextSpan( TextSpan(
text: "0.35",
text: "0.0",
style: TextStyle( style: TextStyle(
fontSize: 23.w, fontSize: 23.w,
) )
), ),
TextSpan( TextSpan(
text: "小时",
text: "",
style: TextStyle( style: TextStyle(
fontSize: 12.w, fontSize: 12.w,
) )
@ -166,7 +168,7 @@ class _SettlementPageState extends State<SettlementPage> {
), ),
), ),
Text( Text(
"今日总开播",
"今日收入",
style: TextStyle( style: TextStyle(
fontSize: 12.w, fontSize: 12.w,
color: const Color.fromRGBO(117, 98, 249, 1) color: const Color.fromRGBO(117, 98, 249, 1)
@ -189,10 +191,29 @@ class _SettlementPageState extends State<SettlementPage> {
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(86.w)), borderRadius: BorderRadius.all(Radius.circular(86.w)),
child: Image.asset(
Assets.imagesUserAvatar,
child: CachedNetworkImage(
imageUrl: "${ GlobalData().userData!.profilePhoto ?? ""}?x-oss-process=image/format,webp/resize,w_240",
width: 86.w, width: 86.w,
height: 86.w, height: 86.w,
fit: BoxFit.cover,
placeholder: (context, url) => Container(
color: Colors.white38,
child: Center(
child: CircularProgressIndicator(
strokeWidth: 1.w,
color: Colors.grey,
),
),
),
errorWidget: (context, url, error) =>
Image.asset(
Assets.imagesUserAvatar,
width: 86.w,
height: 86.w,
fit: BoxFit.cover,
),
), ),
), ),
), ),

3
lib/pages/mine/my_wallet_page.dart

@ -353,12 +353,13 @@ class _InfoItemState extends State<InfoItem> {
widget.item.tradeType == 109 ? "管道收益" : widget.item.tradeType == 109 ? "管道收益" :
widget.item.tradeType == 110 ? "红娘新手任务奖励" : widget.item.tradeType == 110 ? "红娘新手任务奖励" :
widget.item.tradeType == 111 ? "聊天收益" : widget.item.tradeType == 111 ? "聊天收益" :
widget.item.tradeType == 112 ? "礼物收益" :
widget.item.tradeType == 112 ? "聊天礼物收益" :
widget.item.tradeType == 113 ? "邀请分成" : widget.item.tradeType == 113 ? "邀请分成" :
widget.item.tradeType == 114 ? "1V1语音" : widget.item.tradeType == 114 ? "1V1语音" :
widget.item.tradeType == 115 ? "1V1视频" : widget.item.tradeType == 115 ? "1V1视频" :
widget.item.tradeType == 116 ? "连麦收益" : widget.item.tradeType == 116 ? "连麦收益" :
widget.item.tradeType == 117 ? "任务奖励" : widget.item.tradeType == 117 ? "任务奖励" :
widget.item.tradeType == 118 ? "直播礼物收益" :
widget.item.tradeType == 201 ? "平台服务费" : widget.item.tradeType == 201 ? "平台服务费" :
widget.item.tradeType == 202 ? "提现" : ""}", widget.item.tradeType == 202 ? "提现" : ""}",
style: TextStyle( style: TextStyle(

3
lib/rtc/rtc_manager.dart

@ -150,6 +150,9 @@ class RTCManager {
channelName: _currentChannelId ?? '', channelName: _currentChannelId ?? '',
message: json.encode({'type': 'join_room', 'uid': _currentUid}), message: json.encode({'type': 'join_room', 'uid': _currentUid}),
); );
final roomController = Get.find<RoomController>();
roomController.sendChatMessage("新朋友来了");
Get.to(() => const LiveRoomPage(id: 0)); Get.to(() => const LiveRoomPage(id: 0));
} }
} }

8
lib/widget/live/audience_item.dart

@ -8,8 +8,8 @@ import '../../model/discover/audience_list_data.dart';
class AudienceItem extends StatefulWidget { class AudienceItem extends StatefulWidget {
final Records item; final Records item;
final List<String> selectUserId;
final Function(String) selectChange;
final List<Map<String, dynamic>> selectUserId;
final Function(String, int) selectChange;
const AudienceItem({super.key, required this.item, required this.selectUserId, required this.selectChange}); const AudienceItem({super.key, required this.item, required this.selectUserId, required this.selectChange});
@override @override
@ -65,9 +65,9 @@ class _AudienceItemState extends State<AudienceItem> {
), ),
Checkbox( Checkbox(
value: widget.selectUserId.contains(widget.item.userId),
value: widget.selectUserId.indexWhere((e) => e["userId"] == widget.item.userId) != -1,
onChanged: (value) { onChanged: (value) {
widget.selectChange(widget.item.userId ?? "");
widget.selectChange(widget.item.userId ?? "", widget.item.userManagementId ?? 0);
}, },
activeColor: const Color.fromRGBO(117, 98, 249, 1), activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey), side: const BorderSide(color: Colors.grey),

16
lib/widget/live/kick_list.dart

@ -21,14 +21,13 @@ class _KickListState extends State<KickList> {
List<String> selectUserId = [];
List<Map<String, dynamic>> selectUserId = [];
selectChange(String userId){
if (selectUserId.contains(userId)) {
selectUserId.remove(userId);
} else {
selectUserId.add(userId);
}
selectChange(String userId, int uid){
final index = selectUserId.indexWhere((e) => e["userId"] == userId);
index == -1
? selectUserId.add({"userId": userId, "uid": uid})
: selectUserId.removeAt(index);
setState(() { setState(() {
}); });
@ -167,9 +166,8 @@ class _KickListState extends State<KickList> {
), ),
), ),
).onTap(() async { ).onTap(() async {
final selectedA = _roomController.audienceList.where((item) => selectUserId.contains(item.userId)).toList();
final List<String> dList = final List<String> dList =
selectedA.map((item) => item.userManagementId.toString()).toList();
selectUserId.map((item) => item["uid"].toString()).toList();
await _roomController.kickUser(dList); await _roomController.kickUser(dList);
// //

29
lib/widget/live/live_room_anchor_showcase.dart

@ -27,7 +27,7 @@ class _LiveRoomAnchorShowcaseState extends State<LiveRoomAnchorShowcase> {
@override @override
Widget build(BuildContext context) {
build(BuildContext context) {
return ValueListenableBuilder<bool>( return ValueListenableBuilder<bool>(
valueListenable: _rtcManager.channelJoinedNotifier, valueListenable: _rtcManager.channelJoinedNotifier,
builder: (context, joined, _) { builder: (context, joined, _) {
@ -155,7 +155,21 @@ class _LiveRoomAnchorShowcaseState extends State<LiveRoomAnchorShowcase> {
height: 11.w, height: 11.w,
), ),
), ),
),
).onTap(() {
if(_roomController.currentRole == CurrentRole.broadcaster){
_roomController
.rtcChannelDetail
.value?.anchorInfo?.isMicrophoneOn = !(_roomController
.rtcChannelDetail
.value?.anchorInfo?.isMicrophoneOn ?? false);
RTCManager.instance.muteLocalAudio(!(_roomController
.rtcChannelDetail
.value?.anchorInfo?.isMicrophoneOn ?? false));
setState(() {
});
}
}),
], ],
), ),
), ),
@ -367,7 +381,16 @@ class _LiveRoomAnchorShowcaseState extends State<LiveRoomAnchorShowcase> {
height: 11.w, height: 11.w,
), ),
), ),
),
).onTap(() {
if((isLeft && _roomController.currentRole == CurrentRole.maleAudience) || (!isLeft && _roomController.currentRole == CurrentRole.femaleAudience)){
userInfo.isMicrophoneOn = !(userInfo.isMicrophoneOn ?? false);
RTCManager.instance.muteLocalAudio(!(userInfo.isMicrophoneOn ?? false));
setState(() {
});
}
}),
SizedBox(width: 5.w), SizedBox(width: 5.w),
Text( Text(
userInfo.nickName, userInfo.nickName,

5
lib/widget/live/live_room_chat_item.dart

@ -107,7 +107,7 @@ class LiveRoomChatItem extends StatelessWidget {
text: TextSpan( text: TextSpan(
children: [ children: [
TextSpan( TextSpan(
text: "${message.userName}",
text: "${message.userName}${message.content != "新朋友来了" ? "" : " "}",
style: TextStyle( style: TextStyle(
fontSize: 11.w, fontSize: 11.w,
color: const Color.fromRGBO(155, 138, 246, 1), color: const Color.fromRGBO(155, 138, 246, 1),
@ -115,7 +115,8 @@ class LiveRoomChatItem extends StatelessWidget {
), ),
TextSpan( TextSpan(
text: message.content, text: message.content,
style: TextStyle(fontSize: 11.w, color: Colors.white),
style: TextStyle(fontSize: 11.w,
color: message.content != "新朋友来了" ? Colors.white : const Color.fromRGBO(144, 144, 144, 1)),
), ),
], ],
), ),

77
lib/widget/live/live_room_invitation_list.dart

@ -33,14 +33,13 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
_tabController = TabController(length: 3, vsync: this); _tabController = TabController(length: 3, vsync: this);
} }
List<String> selectUserId = [];
selectChange(String userId){
if (selectUserId.contains(userId)) {
selectUserId.remove(userId);
} else {
selectUserId.add(userId);
}
List<Map<String, dynamic>> selectUserId = [];
selectChange(String userId, int uid){
final index = selectUserId.indexWhere((e) => e["userId"] == userId);
index == -1
? selectUserId.add({"userId": userId, "uid": uid})
: selectUserId.removeAt(index);
setState(() { setState(() {
}); });
@ -129,36 +128,38 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
await _roomController.getFriendList(); await _roomController.getFriendList();
} }
}, },
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,
),
child: Obx(() {
return 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,
);
}),
), ),
) )
], ],

141
lib/widget/live/live_room_user_header.dart

@ -1,5 +1,6 @@
import 'package:dating_touchme_app/controller/discover/room_controller.dart'; import 'package:dating_touchme_app/controller/discover/room_controller.dart';
import 'package:dating_touchme_app/controller/overlay_controller.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/generated/assets.dart';
import 'package:dating_touchme_app/pages/discover/live_end_page.dart'; import 'package:dating_touchme_app/pages/discover/live_end_page.dart';
import 'package:dating_touchme_app/pages/discover/settlement_page.dart'; import 'package:dating_touchme_app/pages/discover/settlement_page.dart';
@ -196,20 +197,136 @@ class LiveRoomUserHeader extends StatelessWidget {
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
await roomController.leaveChannel();
await roomController.getDurationMins();
// overlay // overlay
SmartDialog.dismiss(); SmartDialog.dismiss();
// 退RTM消息
if (Get.isRegistered<RoomController>()) {
final roomController = Get.find<RoomController>();
roomController.chatMessages.clear();
}
// pop Get.back()
Get.off(() => SettlementPage());
//
Future.delayed(const Duration(milliseconds: 200), () {
overlayController.hide();
});
SmartDialog.show(
onDismiss: (){
roomController.setDialogDismiss(false);
},
builder: (context) {
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(16.w)),
child: Material(
color: Colors.white,
child: Container(
width: 311.w,
height: 189.w,
padding: EdgeInsets.symmetric(
vertical: 20.w,
horizontal: 27.w
),
child: Column(
children: [
Text(
"您当前正在视频相亲中\n"
"确定要退出吗?",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.w
),
),
Container(
margin: EdgeInsets.only(top: 9.w, bottom: 18.w),
height: 23.w,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft, // 90deg =
end: Alignment.centerRight,
colors: [
Color.fromRGBO(117, 98, 249, 0),
Color.fromRGBO(117, 98, 249, 0.2),
Color.fromRGBO(117, 98, 249, 0.2),
Color.fromRGBO(117, 98, 249, 0.2),
Color.fromRGBO(117, 98, 249, 0),
],
stops: [
0.0,
0.2931,
0.5389,
0.7708,
1.0,
],
),
),
child: Center(
child: Text(
"今日有效露脸开播时长${roomController.durationMins.value}分钟",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(117, 98, 249, 1)
),
),
),
),
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: Center(
child: Text(
"取消",
style: TextStyle(
fontSize: 15.w,
fontWeight: FontWeight.w400
),
),
),
).onTap(() {
SmartDialog.dismiss();
}),
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: Center(
child: Text(
"确认",
style: TextStyle(
color: Colors.white,
fontSize: 15.w,
fontWeight: FontWeight.w400
),
),
),
).onTap(() async {
// 退RTM消息
await roomController.getLiveData();
await roomController.leaveChannel();
SmartDialog.dismiss();
if (Get.isRegistered<RoomController>()) {
final roomController = Get.find<RoomController>();
roomController.chatMessages.clear();
}
// pop Get.back()
Get.off(() => SettlementPage());
//
Future.delayed(const Duration(milliseconds: 200), () {
overlayController.hide();
});
}),
],
)
],
),
),
),
);
},
);
}, },
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,

2
lib/widget/live/live_room_user_profile_dialog.dart

@ -61,7 +61,7 @@ void showUserProfileDialog(
), ),
), ),
).onTap(() async { ).onTap(() async {
await roomController.inviteMic([message.userId]);
await roomController.inviteMic([{"userId": message.userId, "uid": message.uid}]);
// //
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();

Loading…
Cancel
Save