diff --git a/lib/controller/discover/room_controller.dart b/lib/controller/discover/room_controller.dart index d70e2b0..08d332b 100644 --- a/lib/controller/discover/room_controller.dart +++ b/lib/controller/discover/room_controller.dart @@ -1,5 +1,4 @@ import 'package:agora_rtc_engine/agora_rtc_engine.dart'; -import 'package:dating_touchme_app/model/live/live_chat_message.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/network/network_service.dart'; @@ -9,12 +8,14 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; +import '../../model/live/live_chat_message.dart'; + // 当前角色 -enum CurrentRole{ - broadcaster,//主持 - maleAudience,//男嘉宾 - femaleAudience,//女嘉宾 - audience,//观众 +enum CurrentRole { + broadcaster, //主持 + maleAudience, //男嘉宾 + femaleAudience, //女嘉宾 + audience, //观众 normalUser, //普通用户 } @@ -26,6 +27,7 @@ class RoomController extends GetxController { final NetworkService _networkService; CurrentRole currentRole = CurrentRole.normalUser; bool isLive = false; + /// 当前频道信息 final Rxn rtcChannel = Rxn(); final Rxn rtcChannelDetail = Rxn(); @@ -158,7 +160,10 @@ class RoomController extends GetxController { 'channelId': rtcChannel.value?.channelId, 'seatNumber': role == CurrentRole.maleAudience ? 1 : 2, 'isMicrophoneOn': role != CurrentRole.normalUser ? true : false, - 'isVideoOn': role == CurrentRole.maleAudience || role == CurrentRole.femaleAudience ? true : false, + 'isVideoOn': + role == CurrentRole.maleAudience || role == CurrentRole.femaleAudience + ? true + : false, }; final response = await _networkService.rtcApi.connectRtcChannel(data); if (!response.data.isSuccess) { @@ -166,21 +171,38 @@ class RoomController extends GetxController { return; } currentRole = role; - if(role == CurrentRole.maleAudience || role == CurrentRole.femaleAudience){ + if (role == CurrentRole.maleAudience || + role == CurrentRole.femaleAudience) { await RTCManager.instance.publishVideo(role); - }else{ + } else { await RTCManager.instance.publishAudio(); } isLive = true; } Future leaveChat() async { - final data = { - 'channelId': rtcChannel.value?.channelId - }; + final data = {'channelId': rtcChannel.value?.channelId}; final response = await _networkService.rtcApi.disconnectRtcChannel(data); - if(response.data.isSuccess){ + if (response.data.isSuccess) { + isLive = false; await RTCManager.instance.unpublish(); + if (currentRole == CurrentRole.maleAudience) { + final newDetail = RtcChannelDetail( + channelId: rtcChannelDetail.value!.channelId, + anchorInfo: rtcChannelDetail.value!.anchorInfo, + maleInfo: null, + femaleInfo: rtcChannelDetail.value!.femaleInfo, + ); + rtcChannelDetail.value = newDetail; + } else if (currentRole == CurrentRole.femaleAudience) { + final newDetail = RtcChannelDetail( + channelId: rtcChannelDetail.value!.channelId, + anchorInfo: rtcChannelDetail.value!.anchorInfo, + maleInfo: rtcChannelDetail.value!.maleInfo, + femaleInfo: null, + ); + rtcChannelDetail.value = newDetail; + } } } @@ -234,6 +256,94 @@ class RoomController extends GetxController { } Future leaveChannel() async { + isLive = false; + currentRole = CurrentRole.normalUser;; await RTCManager.instance.leaveChannel(); } + + /// 接收RTC消息 + Future receiveRTCMessage(Map message) async { + if (message['type'] == 'join_chat') { + final response = await _networkService.rtcApi.getDatingRtcChannelUserDetail( + rtcChannel.value!.channelId, + message['uid'], + ); + if (!response.data.isSuccess) { + return; + } + final currentDetail = rtcChannelDetail.value; + if (currentDetail == null) { + return; + } + if (message['role'] == 'male_audience') { + final userData = response.data.data; + // if (userData != null) { + final maleInfo = RtcSeatUserInfo( + miId: rtcChannelDetail.value!.anchorInfo!.miId, + userId: rtcChannelDetail.value!.anchorInfo!.userId, + nickName: rtcChannelDetail.value!.anchorInfo!.nickName, + profilePhoto: rtcChannelDetail.value!.anchorInfo!.profilePhoto, + genderCode: rtcChannelDetail.value!.anchorInfo!.genderCode, + seatNumber: rtcChannelDetail.value!.anchorInfo!.seatNumber, + isFriend: rtcChannelDetail.value!.anchorInfo!.isFriend, + isMicrophoneOn: rtcChannelDetail.value!.anchorInfo!.isMicrophoneOn, + isVideoOn: rtcChannelDetail.value!.anchorInfo!.isVideoOn, + uid: message['uid'] is int + ? message['uid'] as int + : int.tryParse(message['uid']?.toString() ?? ''), + ); + final newDetail = RtcChannelDetail( + channelId: currentDetail.channelId, + anchorInfo: currentDetail.anchorInfo, + maleInfo: maleInfo, + femaleInfo: currentDetail.femaleInfo, + ); + rtcChannelDetail.value = newDetail; + // } + } else if (message['role'] == 'female_audience') { + final userData = response.data.data; + // if (userData != null) { + final femaleInfo = RtcSeatUserInfo( + miId: rtcChannelDetail.value!.anchorInfo!.miId, + userId: rtcChannelDetail.value!.anchorInfo!.userId, + nickName: rtcChannelDetail.value!.anchorInfo!.nickName, + profilePhoto: rtcChannelDetail.value!.anchorInfo!.profilePhoto, + genderCode: rtcChannelDetail.value!.anchorInfo!.genderCode, + seatNumber: rtcChannelDetail.value!.anchorInfo!.seatNumber, + isFriend: rtcChannelDetail.value!.anchorInfo!.isFriend, + isMicrophoneOn: rtcChannelDetail.value!.anchorInfo!.isMicrophoneOn, + isVideoOn: rtcChannelDetail.value!.anchorInfo!.isVideoOn, + uid: message['uid'] is int + ? message['uid'] as int + : int.tryParse(message['uid']?.toString() ?? ''), + ); + final newDetail = RtcChannelDetail( + channelId: currentDetail.channelId, + anchorInfo: currentDetail.anchorInfo, + maleInfo: currentDetail.maleInfo, + femaleInfo: femaleInfo, + ); + rtcChannelDetail.value = newDetail; + } + // } + }else if (message['type'] == 'leave_chat') { + if (message['role'] == 'male_audience') { + final newDetail = RtcChannelDetail( + channelId: rtcChannelDetail.value!.channelId, + anchorInfo: rtcChannelDetail.value!.anchorInfo, + maleInfo: null, + femaleInfo: rtcChannelDetail.value!.femaleInfo, + ); + rtcChannelDetail.value = newDetail; + } else if (message['role'] == 'female_audience') { + final newDetail = RtcChannelDetail( + channelId: rtcChannelDetail.value!.channelId, + anchorInfo: rtcChannelDetail.value!.anchorInfo, + maleInfo: rtcChannelDetail.value!.maleInfo?.uid != message['uid'] ? rtcChannelDetail.value!.maleInfo : null, + femaleInfo: rtcChannelDetail.value!.femaleInfo?.uid != message['uid'] ? rtcChannelDetail.value!.femaleInfo : null, + ); + rtcChannelDetail.value = newDetail; + } + } + } } diff --git a/lib/network/rtc_api.dart b/lib/network/rtc_api.dart index f68905f..1d3cba6 100644 --- a/lib/network/rtc_api.dart +++ b/lib/network/rtc_api.dart @@ -40,9 +40,9 @@ abstract class RtcApi { /// 获取 RTC 频道用户详情 @GET(ApiUrls.getDatingRtcChannelUserDetail) - Future>> getDatingRtcChannelUserDetail( + Future>> getDatingRtcChannelUserDetail( @Query('channelId') String channelId, - @Query('uId') String uId, + @Query('uId') int uId, ); /// 启用/禁用 RTC 频道用户音频 diff --git a/lib/network/rtc_api.g.dart b/lib/network/rtc_api.g.dart index de382ed..3f0d213 100644 --- a/lib/network/rtc_api.g.dart +++ b/lib/network/rtc_api.g.dart @@ -185,10 +185,8 @@ class _RtcApi implements RtcApi { } @override - Future>> getDatingRtcChannelUserDetail( - String channelId, - String uId, - ) async { + Future>> + getDatingRtcChannelUserDetail(String channelId, int uId) async { final _extra = {}; final queryParameters = { r'channelId': channelId, @@ -196,7 +194,7 @@ class _RtcApi implements RtcApi { }; final _headers = {}; const Map? _data = null; - final _options = _setStreamType>>( + final _options = _setStreamType>>( Options(method: 'GET', headers: _headers, extra: _extra) .compose( _dio.options, @@ -207,11 +205,11 @@ class _RtcApi implements RtcApi { .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), ); final _result = await _dio.fetch>(_options); - late BaseResponse _value; + late BaseResponse _value; try { - _value = BaseResponse.fromJson( + _value = BaseResponse.fromJson( _result.data!, - (json) => json as dynamic, + (json) => RtcSeatUserInfo.fromJson(json as Map), ); } on Object catch (e, s) { errorLogger?.logError(e, s, _options); diff --git a/lib/rtc/rtc_manager.dart b/lib/rtc/rtc_manager.dart index ad99562..c37ad65 100644 --- a/lib/rtc/rtc_manager.dart +++ b/lib/rtc/rtc_manager.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import '../controller/discover/room_controller.dart'; +import '../network/network_service.dart'; import '../pages/discover/live_room_page.dart'; /// RTC 管理器,负责管理声网音视频通话功能 @@ -15,6 +16,7 @@ class RTCManager { final ValueNotifier> remoteUsersNotifier = ValueNotifier>( [], ); + NetworkService get _networkService => NetworkService(); RtcEngine? get engine => _engine; bool get isInChannel => _isInChannel; int? get currentUid => _currentUid; @@ -119,7 +121,6 @@ class RTCManager { _currentChannelId = connection.channelId; print('加入频道成功,频道名:${connection.channelId},耗时:${elapsed}ms'); if (connection.localUid == _currentUid) { - await RTMManager.instance.subscribe(_currentChannelId ?? ''); await RTMManager.instance.publishChannelMessage( channelName: _currentChannelId ?? '', @@ -131,7 +132,7 @@ class RTCManager { onJoinChannelSuccess!(connection, elapsed); } }, - onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) { + onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) async{ print('用户加入,UID:$remoteUid'); _handleRemoteUserJoined(remoteUid); if (onUserJoined != null) { @@ -483,7 +484,7 @@ class RTCManager { } /// 发布视频 - Future publishVideo(CurrentRole role) async { + Future publishVideo(CurrentRole role) async { await _engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); await _engine?.muteLocalAudioStream(false); await _engine?.muteLocalVideoStream(false); @@ -492,7 +493,9 @@ class RTCManager { message: json.encode({ 'type': 'join_chat', 'uid': _currentUid, - 'role': role == CurrentRole.maleAudience ? 'male_audience' : 'female_audience' + 'role': role == CurrentRole.maleAudience + ? 'male_audience' + : 'female_audience', }), ); } @@ -507,7 +510,7 @@ class RTCManager { message: json.encode({ 'type': 'join_chat', 'uid': _currentUid, - 'role': 'audience' + 'role': 'audience', }), ); } @@ -519,10 +522,7 @@ class RTCManager { await _engine?.muteLocalVideoStream(true); await RTMManager.instance.publishChannelMessage( channelName: _currentChannelId ?? '', - message: json.encode({ - 'type': 'leave_chat', - 'uid': _currentUid, - }), + message: json.encode({'type': 'leave_chat', 'uid': _currentUid}), ); } } diff --git a/lib/service/live_chat_message_service.dart b/lib/service/live_chat_message_service.dart index 8c5f7b0..c4d0323 100644 --- a/lib/service/live_chat_message_service.dart +++ b/lib/service/live_chat_message_service.dart @@ -1,11 +1,13 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:agora_rtm/agora_rtm.dart'; +import 'package:dating_touchme_app/controller/discover/room_controller.dart'; import 'package:dating_touchme_app/controller/global.dart'; import 'package:dating_touchme_app/model/live/live_chat_message.dart'; import 'package:dating_touchme_app/rtc/rtc_manager.dart'; import 'package:dating_touchme_app/rtc/rtm_manager.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; /// 直播间聊天消息服务 @@ -56,7 +58,7 @@ class LiveChatMessageService { } /// 处理接收到的消息 - void _handleIncomingMessage(MessageEvent event) { + void _handleIncomingMessage(MessageEvent event) async{ try { // 解析消息内容 final messageText = _parseMessageContent(event.message); @@ -66,8 +68,9 @@ class LiveChatMessageService { if (messageData['type'] == 'chat_message') { final chatMessage = LiveChatMessage.fromJson(messageData); onMessageReceived?.call(chatMessage); - }else if(messageData['type'] == 'like_message'){ - + }else{ + RoomController controller = Get.find(); + await controller.receiveRTCMessage(messageData); } } catch (e, stackTrace) { final error = '解析RTM消息失败: $e'; diff --git a/lib/widget/live/live_room_anchor_showcase.dart b/lib/widget/live/live_room_anchor_showcase.dart index 4073fa1..54b0f5c 100644 --- a/lib/widget/live/live_room_anchor_showcase.dart +++ b/lib/widget/live/live_room_anchor_showcase.dart @@ -100,6 +100,8 @@ class _LiveRoomAnchorShowcaseState extends State { ), SizedBox(height: 5.w), Obx(() { + // 直接访问响应式变量以触发更新 + _roomController.rtcChannelDetail.value; final rtcChannelDetail = _roomController.rtcChannelDetail.value; return Row( @@ -188,7 +190,9 @@ class _LiveRoomAnchorShowcaseState extends State { final joined = _rtcManager.channelJoinedNotifier.value; // 判断是否是当前用户 - final bool isCurrentUser = _roomController.currentRole == CurrentRole.maleAudience || _roomController.currentRole == CurrentRole.femaleAudience; + final bool isCurrentUser = + _roomController.currentRole == CurrentRole.maleAudience || + _roomController.currentRole == CurrentRole.femaleAudience; return Stack( children: [ diff --git a/lib/widget/live/live_room_notice_chat_panel.dart b/lib/widget/live/live_room_notice_chat_panel.dart index e542959..7a3f026 100644 --- a/lib/widget/live/live_room_notice_chat_panel.dart +++ b/lib/widget/live/live_room_notice_chat_panel.dart @@ -76,10 +76,6 @@ class _LiveRoomNoticeChatPanelState extends State { ), SizedBox(width: 18.w), Obx((){ - Get.log("${controller.rtcChannelDetail.value?.maleInfo}"); - Get.log("${controller.rtcChannelDetail.value?.femaleInfo}"); - Get.log("${controller.currentRole}"); - Get.log("${GlobalData().userData?.genderCode}"); if(controller.rtcChannelDetail.value?.maleInfo == null && GlobalData().userData?.genderCode == 0 && controller.currentRole != CurrentRole.broadcaster || controller.rtcChannelDetail.value?.femaleInfo == null && GlobalData().userData?.genderCode == 1 && controller.currentRole != CurrentRole.broadcaster){ return Container( @@ -88,8 +84,8 @@ class _LiveRoomNoticeChatPanelState extends State { padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.w), - gradient: const LinearGradient( - colors: [Color(0xFF7C63FF), Color(0xFF987CFF)], + gradient: LinearGradient( + colors: controller.isLive ? [Colors.grey] : [Color(0xFF7C63FF), Color(0xFF987CFF)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), @@ -106,7 +102,7 @@ class _LiveRoomNoticeChatPanelState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - '免费连麦', + controller.isLive ? '申请连麦中' : '免费连麦', style: TextStyle( fontSize: 13.w, color: Colors.white, @@ -114,7 +110,7 @@ class _LiveRoomNoticeChatPanelState extends State { ), ), SizedBox(height: 2.w), - Text( + controller.isLive ? const SizedBox() :Text( '剩余2张相亲卡', style: TextStyle( fontSize: 9.w,