Browse Source

refactor(call): 重构通话功能实现并优化视频通话页面

- 修正音频和视频通话类型参数,音频改为type:2,视频改为type:3
- 在通话信息中添加uid字段并传递给通话控制器
- 移除通话状态更新相关代码,不再修改消息状态
- 从IMManager中移除modifyMessage方法实现
- 重构视频通话页面背景构建逻辑,优化远端用户UID监听机制
- 添加key确保remoteUid变化时视频视图正确重建
master
Jolie 3 months ago
parent
commit
b1cb74f23c
4 changed files with 75 additions and 266 deletions
  1. 131
      lib/controller/message/call_controller.dart
  2. 2
      lib/controller/message/chat_controller.dart
  3. 92
      lib/im/im_manager.dart
  4. 116
      lib/pages/message/video_call_page.dart

131
lib/controller/message/call_controller.dart

@ -205,12 +205,12 @@ class CallController extends GetxController {
///
Future<RtcChannelData?> createAudioChannel() {
return createOneOnOneRtcChannel(type: 1);
return createOneOnOneRtcChannel(type: 2);
}
///
Future<RtcChannelData?> createVideoChannel() {
return createOneOnOneRtcChannel(type: 2);
return createOneOnOneRtcChannel(type: 3);
}
///
@ -266,6 +266,7 @@ class CallController extends GetxController {
callStatus: 'waitCalling', //
channelId: channelData.channelId, // ID
chatController: chatController,
uid: channelData.uid,
);
startCallAudio();
@ -339,15 +340,7 @@ class CallController extends GetxController {
//
_startCallTimer();
//
await _updateCallMessageStatus(
message: message,
callStatus: 'calling',
callDuration: 0,
chatController: chatController,
);
// channelId
// channelId uid
final channelId = callInfo['channelId'] as String?;
if (channelId == null || channelId.isEmpty) {
print('❌ [CallController] channelId 为空,无法加入 RTC 频道');
@ -373,6 +366,13 @@ class CallController extends GetxController {
await joinChannel(channelId);
print('✅ [CallController] 已加入 RTC 频道: $channelId');
// RTC uid UID
final initiatorUid = callInfo['uid'] as int?;
if (initiatorUid != null) {
remoteUid.value = initiatorUid;
print('📞 [CallController] 从消息中获取到发起方 UID: $initiatorUid,已设置 remoteUid');
}
return true;
}
@ -388,13 +388,6 @@ class CallController extends GetxController {
//
stopCallAudio();
//
await _updateCallMessageStatus(
message: message,
callStatus: 'rejected',
chatController: chatController,
);
//
currentCall.value = null;
@ -413,15 +406,6 @@ class CallController extends GetxController {
}) async {
print('📞 [CallController] 取消通话');
//
if (message != null) {
await _updateCallMessageStatus(
message: message,
callStatus: 'cancelled',
chatController: chatController,
);
}
//
stopCallAudio();
@ -453,16 +437,6 @@ class CallController extends GetxController {
//
_stopCallTimer();
//
if (message != null) {
await _updateCallMessageStatus(
message: message,
callStatus: 'calling',
callDuration: callDuration,
chatController: chatController,
);
}
// UID
currentCall.value = null;
remoteUid.value = null;
@ -502,6 +476,7 @@ class CallController extends GetxController {
required String callType,
required String callStatus,
int? callDuration,
int? uid,
String? channelId,
ChatController? chatController,
}) async {
@ -511,89 +486,10 @@ class CallController extends GetxController {
callStatus: callStatus,
callDuration: callDuration,
channelId: channelId,
uid: uid,
);
}
/// 使modifyMessage修改现有消息
Future<bool> _updateCallMessageStatus({
required EMMessage message,
required String callStatus,
int? callDuration,
ChatController? chatController,
}) async {
//
final callInfo = _parseCallInfo(message);
if (callInfo == null) {
return false;
}
final callType = callInfo['callType'] as String? ?? 'voice';
final messageId = message.msgId;
if (messageId.isEmpty) {
print('❌ [CallController] 消息ID为空,无法修改消息');
return false;
}
// 使modifyMessage修改
if (message.body.type == MessageType.CUSTOM) {
//
final callParams = <String, String>{
'callType': callType,
'callStatus': callStatus,
};
if (callDuration != null) {
callParams['callDuration'] = callDuration.toString();
}
//
final customBody = EMCustomMessageBody(event: 'call', params: callParams);
// 使modifyMessage修改消息
final success = await IMManager.instance.modifyMessage(
messageId: messageId,
msgBody: customBody,
attributes: null, //
);
if (success) {
print(
'✅ [CallController] 消息修改成功: messageId=$messageId, callStatus=$callStatus',
);
// chatController
if (chatController != null) {
//
final index = chatController.messages.indexWhere(
(msg) => msg.msgId == messageId,
);
if (index != -1) {
final updatedMessage = chatController.messages[index];
if (updatedMessage.body.type == MessageType.CUSTOM) {
final customBody = updatedMessage.body as EMCustomMessageBody;
// Map并更新
final updatedParams = Map<String, String>.from(
customBody.params ?? {},
);
updatedParams['callType'] = callType;
updatedParams['callStatus'] = callStatus;
if (callDuration != null) {
updatedParams['callDuration'] = callDuration.toString();
}
// EMCustomMessageBody的params可能是只读的
// UI更新onMessageContentChanged回调时处理
chatController.update();
}
}
}
}
return success;
}
//
return false;
}
///
Map<String, dynamic>? _parseCallInfo(EMMessage message) {
if (message.body.type == MessageType.CUSTOM) {
@ -607,6 +503,7 @@ class CallController extends GetxController {
? int.tryParse(params['callDuration']!)
: null,
'channelId': params['channelId'],
'uid': params['uid'] != null ? int.tryParse(params['uid']!) : null,
};
}
}

2
lib/controller/message/chat_controller.dart

@ -1059,12 +1059,14 @@ class ChatController extends GetxController {
required String callStatus, // 'calling', 'missed', 'cancelled', 'rejected'
int? callDuration, //
String? channelId, // RTC频道ID
int? uid
}) async {
try {
// Map<String, String>
final callParams = <String, String>{
'callType': callType,
'callStatus': callStatus,
'uid': uid.toString(),
};
if (callDuration != null) {
callParams['callDuration'] = callDuration.toString();

92
lib/im/im_manager.dart

@ -1856,98 +1856,6 @@ class IMManager {
}
}
///
/// [messageId] ID
/// [msgBody] null则不修改消息体
/// [attributes] null则不修改扩展属性
Future<bool> modifyMessage({
required String messageId,
EMMessageBody? msgBody,
Map<String, String>? attributes,
}) async {
try {
if (messageId.isEmpty) {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] 消息ID为空,无法修改');
}
return false;
}
//
if (msgBody == null && attributes == null) {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] 消息体和扩展属性都为空,无法修改');
}
return false;
}
// SDK的修改消息方法
await EMClient.getInstance.chatManager.modifyMessage(
messageId: messageId,
msgBody: msgBody,
attributes: attributes,
);
//
_refreshConversationList();
// ChatController
_notifyMessageModified(messageId, msgBody, attributes);
return true;
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] 消息修改失败: messageId=$messageId, 错误: $e');
}
return false;
}
}
/// ChatController
void _notifyMessageModified(
String messageId,
EMMessageBody? msgBody,
Map<String, String>? attributes,
) {
try {
// ChatController
for (var entry in _activeChatControllers.entries) {
final controller = entry.value;
final index = controller.messages.indexWhere((msg) => msg.msgId == messageId);
if (index != -1) {
//
final message = controller.messages[index];
//
if (msgBody != null) {
// EMMessage body
//
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] 消息体修改需要重新获取消息: messageId=$messageId');
}
}
//
if (attributes != null) {
message.attributes ??= {};
message.attributes!.addAll(attributes);
}
// UI更新
controller.update();
if (Get.isLogEnable) {
Get.log('✅ [IMManager] 已通知ChatController更新消息: userId=${entry.key}, messageId=$messageId');
}
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] 通知ChatController消息修改失败: $e');
}
}
}
///
Future<bool> recallMessage(EMMessage message) async {
try {

116
lib/pages/message/video_call_page.dart

@ -293,70 +293,72 @@ class _VideoCallPageState extends State<VideoCallPage> {
///
Widget _buildBackground() {
final callSession = _callController.currentCall.value;
final isVideoCall = callSession != null && callSession.callType == CallType.video;
// 使 Obx UID
if (isVideoCall) {
return Obx(() {
// CallController.remoteUid RTCManager.remoteUsersNotifier
var remoteUid = _callController.remoteUid.value;
final remoteUsers = _rtcManager.remoteUsersNotifier.value; // Obx
print('📞 [VideoCallPage] Obx 重建,CallController.remoteUid: ${_callController.remoteUid.value}, remoteUsers: $remoteUsers, isVideoCall: $isVideoCall');
// remoteUid RTCManager
if (remoteUid == null && remoteUsers.isNotEmpty) {
remoteUid = remoteUsers.first;
// CallController
_callController.remoteUid.value = remoteUid;
print('📞 [VideoCallPage] 从 RTCManager.remoteUsersNotifier 获取到 remoteUid: $remoteUid');
}
//
if (remoteUid != null) {
final engine = _rtcManager.engine;
print('📞 [VideoCallPage] remoteUid 不为 null: $remoteUid, engine: ${engine != null}');
if (engine != null) {
print('📞 [VideoCallPage] 显示远端视频视图,UID:$remoteUid');
final remoteVideoViewController = VideoViewController(
rtcEngine: engine,
canvas: VideoCanvas(uid: remoteUid),
);
return SizedBox(
width: double.infinity,
height: 1.sh,
child: AgoraVideoView(
controller: remoteVideoViewController,
),
);
} else {
print('⚠️ [VideoCallPage] engine 为 null,无法显示远端视频');
}
} else {
print('⚠️ [VideoCallPage] remoteUid 为 null,无法显示远端视频');
}
//
if (_localVideoViewController != null) {
print('📞 [VideoCallPage] 显示本地视频视图');
// 使 Obx UID
return Obx(() {
// Obx 访
final callSession = _callController.currentCall.value;
final isVideoCall = callSession != null && callSession.callType == CallType.video;
final remoteUid = _callController.remoteUid.value;
final remoteUsers = _rtcManager.remoteUsersNotifier.value;
print('📞 [VideoCallPage] _buildBackground Obx 重建,isVideoCall: $isVideoCall, remoteUid: $remoteUid, remoteUsers: $remoteUsers');
//
if (!isVideoCall) {
return _buildAvatarBackground();
}
// remoteUid RTCManager
if (remoteUid == null && remoteUsers.isNotEmpty) {
_callController.remoteUid.value = remoteUsers.first;
print('📞 [VideoCallPage] 从 RTCManager.remoteUsersNotifier 获取到 remoteUid: ${remoteUsers.first}');
// Obx
}
// remoteUid
final currentRemoteUid = _callController.remoteUid.value;
//
if (currentRemoteUid != null) {
final engine = _rtcManager.engine;
print('📞 [VideoCallPage] currentRemoteUid 不为 null: $currentRemoteUid, engine: ${engine != null}');
if (engine != null) {
print('📞 [VideoCallPage] 显示远端视频视图,UID:$currentRemoteUid');
final remoteVideoViewController = VideoViewController(
rtcEngine: engine,
canvas: VideoCanvas(uid: currentRemoteUid),
);
return SizedBox(
width: double.infinity,
height: 1.sh,
key: ValueKey('remote_video_$currentRemoteUid'), // 使 key remoteUid
child: AgoraVideoView(
controller: _localVideoViewController!,
controller: remoteVideoViewController,
),
);
} else {
print('⚠️ [VideoCallPage] engine 为 null,无法显示远端视频');
}
//
print('📞 [VideoCallPage] 显示头像背景');
return _buildAvatarBackground();
});
}
//
return _buildAvatarBackground();
} else {
print('⚠️ [VideoCallPage] currentRemoteUid 为 null,无法显示远端视频');
}
//
if (_localVideoViewController != null) {
print('📞 [VideoCallPage] 显示本地视频视图');
return SizedBox(
width: double.infinity,
height: 1.sh,
child: AgoraVideoView(
controller: _localVideoViewController!,
),
);
}
//
print('📞 [VideoCallPage] 显示头像背景');
return _buildAvatarBackground();
});
}
///

Loading…
Cancel
Save