Browse Source

refactor(video_call): 优化视频通话页面代码结构和功能

- 修复了本地视频视图初始化时机问题,确保接收方接听后正确显示
- 添加了本地视频预览小窗口功能,右上角显示当前通话的本地视频
- 优化了代码格式和布局,统一了缩进和换行规范
- 重构了背景视频构建逻辑,改进了远程用户ID获取机制
- 调整了用户信息显示逻辑,视频通话接通后隐藏头像和昵称
- 优化了控制按钮颜色逻辑,根据激活状态动态调整按钮样式
- 修复了系统UI模式设置参数,确保正确的全屏和方向控制
- 增强了通话状态监听和控制按钮的显示/隐藏逻辑
master
Jolie 3 months ago
parent
commit
76bb0e68c2
1 changed files with 149 additions and 79 deletions
  1. 228
      lib/pages/message/video_call_page.dart

228
lib/pages/message/video_call_page.dart

@ -42,16 +42,16 @@ class VideoCallPage extends StatefulWidget {
class _VideoCallPageState extends State<VideoCallPage> { class _VideoCallPageState extends State<VideoCallPage> {
final CallController _callController = CallController.instance; final CallController _callController = CallController.instance;
final RTCManager _rtcManager = RTCManager.instance; final RTCManager _rtcManager = RTCManager.instance;
Timer? _durationTimer; Timer? _durationTimer;
String? _targetUserName; String? _targetUserName;
String? _targetAvatarUrl; String? _targetAvatarUrl;
// 5 // 5
final RxBool showControls = true.obs; final RxBool showControls = true.obs;
Timer? _hideControlsTimer; Timer? _hideControlsTimer;
// //
VideoViewController? _localVideoViewController; VideoViewController? _localVideoViewController;
@ -62,14 +62,12 @@ class _VideoCallPageState extends State<VideoCallPage> {
_initCallStatus(); _initCallStatus();
_startDurationTimer(); _startDurationTimer();
_initLocalVideo(); _initLocalVideo();
// UI样式 // UI样式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
} }
/// ///
void _initLocalVideo() { void _initLocalVideo() {
final callSession = _callController.currentCall.value; final callSession = _callController.currentCall.value;
@ -89,7 +87,7 @@ class _VideoCallPageState extends State<VideoCallPage> {
void _initCallStatus() { void _initCallStatus() {
// 使 CallController // 使 CallController
} }
/// ///
bool get _isCallConnected { bool get _isCallConnected {
final callSession = _callController.currentCall.value; final callSession = _callController.currentCall.value;
@ -101,8 +99,8 @@ class _VideoCallPageState extends State<VideoCallPage> {
// 使 userData // 使 userData
if (widget.userData != null) { if (widget.userData != null) {
setState(() { setState(() {
_targetUserName = widget.userData!.nickName.isNotEmpty
? widget.userData!.nickName
_targetUserName = widget.userData!.nickName.isNotEmpty
? widget.userData!.nickName
: widget.targetUserId; : widget.targetUserId;
_targetAvatarUrl = widget.userData!.profilePhoto; _targetAvatarUrl = widget.userData!.profilePhoto;
}); });
@ -112,10 +110,14 @@ class _VideoCallPageState extends State<VideoCallPage> {
// userData ConversationController // userData ConversationController
if (Get.isRegistered<ConversationController>()) { if (Get.isRegistered<ConversationController>()) {
final conversationController = Get.find<ConversationController>(); final conversationController = Get.find<ConversationController>();
// //
final cachedUserInfo = conversationController.getCachedUserInfo(widget.targetUserId);
if (cachedUserInfo != null && (cachedUserInfo.nickName != null || cachedUserInfo.avatarUrl != null)) {
final cachedUserInfo = conversationController.getCachedUserInfo(
widget.targetUserId,
);
if (cachedUserInfo != null &&
(cachedUserInfo.nickName != null ||
cachedUserInfo.avatarUrl != null)) {
setState(() { setState(() {
_targetUserName = cachedUserInfo.nickName ?? widget.targetUserId; _targetUserName = cachedUserInfo.nickName ?? widget.targetUserId;
_targetAvatarUrl = cachedUserInfo.avatarUrl; _targetAvatarUrl = cachedUserInfo.avatarUrl;
@ -124,8 +126,11 @@ class _VideoCallPageState extends State<VideoCallPage> {
} }
// IM // IM
final userInfo = await conversationController.loadContact(widget.targetUserId);
if (userInfo != null && (userInfo.nickName != null || userInfo.avatarUrl != null)) {
final userInfo = await conversationController.loadContact(
widget.targetUserId,
);
if (userInfo != null &&
(userInfo.nickName != null || userInfo.avatarUrl != null)) {
setState(() { setState(() {
_targetUserName = userInfo.nickName ?? widget.targetUserId; _targetUserName = userInfo.nickName ?? widget.targetUserId;
_targetAvatarUrl = userInfo.avatarUrl; _targetAvatarUrl = userInfo.avatarUrl;
@ -146,7 +151,10 @@ class _VideoCallPageState extends State<VideoCallPage> {
_durationTimer?.cancel(); _durationTimer?.cancel();
_hideControlsTimer?.cancel(); _hideControlsTimer?.cancel();
_localVideoViewController?.dispose(); _localVideoViewController?.dispose();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
SystemChrome.setPreferredOrientations(DeviceOrientation.values); SystemChrome.setPreferredOrientations(DeviceOrientation.values);
super.dispose(); super.dispose();
} }
@ -158,7 +166,8 @@ class _VideoCallPageState extends State<VideoCallPage> {
if (mounted) { if (mounted) {
final wasConnected = _isCallConnected; final wasConnected = _isCallConnected;
// //
if (callSession != null && _callController.callDurationSeconds.value > 0) {
if (callSession != null &&
_callController.callDurationSeconds.value > 0) {
if (!wasConnected) { if (!wasConnected) {
// 5 // 5
_startHideControlsTimer(); _startHideControlsTimer();
@ -169,7 +178,7 @@ class _VideoCallPageState extends State<VideoCallPage> {
} }
} }
}); });
// //
_callController.callDurationSeconds.listen((seconds) { _callController.callDurationSeconds.listen((seconds) {
if (mounted && !_isCallConnected && seconds > 0) { if (mounted && !_isCallConnected && seconds > 0) {
@ -193,9 +202,9 @@ class _VideoCallPageState extends State<VideoCallPage> {
/// / /// /
void _toggleControlsVisibility() { void _toggleControlsVisibility() {
if (!_isCallConnected) return; // if (!_isCallConnected) return; //
showControls.value = !showControls.value; showControls.value = !showControls.value;
// 5 // 5
if (showControls.value) { if (showControls.value) {
_startHideControlsTimer(); _startHideControlsTimer();
@ -245,16 +254,19 @@ class _VideoCallPageState extends State<VideoCallPage> {
children: [ children: [
// / // /
_buildBackground(), _buildBackground(),
// //
_buildMinimizeButton(), _buildMinimizeButton(),
//
_buildLocalVideoPreview(),
// //
_buildUserInfo(), _buildUserInfo(),
// //
_buildCallDuration(), _buildCallDuration(),
// //
_buildControlButtons(), _buildControlButtons(),
], ],
@ -297,31 +309,38 @@ class _VideoCallPageState extends State<VideoCallPage> {
return Obx(() { return Obx(() {
// Obx 访 // Obx 访
final callSession = _callController.currentCall.value; final callSession = _callController.currentCall.value;
final isVideoCall = callSession != null && callSession.callType == CallType.video;
final isVideoCall =
callSession != null && callSession.callType == CallType.video;
final remoteUid = _callController.remoteUid.value; final remoteUid = _callController.remoteUid.value;
final remoteUsers = _rtcManager.remoteUsersNotifier.value; final remoteUsers = _rtcManager.remoteUsersNotifier.value;
print('📞 [VideoCallPage] _buildBackground Obx 重建,isVideoCall: $isVideoCall, remoteUid: $remoteUid, remoteUsers: $remoteUsers');
print(
'📞 [VideoCallPage] _buildBackground Obx 重建,isVideoCall: $isVideoCall, remoteUid: $remoteUid, remoteUsers: $remoteUsers',
);
// //
if (!isVideoCall) { if (!isVideoCall) {
return _buildAvatarBackground(); return _buildAvatarBackground();
} }
// remoteUid RTCManager // remoteUid RTCManager
if (remoteUid == null && remoteUsers.isNotEmpty) { if (remoteUid == null && remoteUsers.isNotEmpty) {
_callController.remoteUid.value = remoteUsers.first; _callController.remoteUid.value = remoteUsers.first;
print('📞 [VideoCallPage] 从 RTCManager.remoteUsersNotifier 获取到 remoteUid: ${remoteUsers.first}');
print(
'📞 [VideoCallPage] 从 RTCManager.remoteUsersNotifier 获取到 remoteUid: ${remoteUsers.first}',
);
// Obx // Obx
} }
// remoteUid // remoteUid
final currentRemoteUid = _callController.remoteUid.value; final currentRemoteUid = _callController.remoteUid.value;
// //
if (currentRemoteUid != null) { if (currentRemoteUid != null) {
final engine = _rtcManager.engine; final engine = _rtcManager.engine;
print('📞 [VideoCallPage] currentRemoteUid 不为 null: $currentRemoteUid, engine: ${engine != null}');
print(
'📞 [VideoCallPage] currentRemoteUid 不为 null: $currentRemoteUid, engine: ${engine != null}',
);
if (engine != null) { if (engine != null) {
print('📞 [VideoCallPage] 显示远端视频视图,UID:$currentRemoteUid'); print('📞 [VideoCallPage] 显示远端视频视图,UID:$currentRemoteUid');
final remoteVideoViewController = VideoViewController( final remoteVideoViewController = VideoViewController(
@ -331,10 +350,10 @@ class _VideoCallPageState extends State<VideoCallPage> {
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
height: 1.sh, height: 1.sh,
key: ValueKey('remote_video_$currentRemoteUid'), // 使 key remoteUid
child: AgoraVideoView(
controller: remoteVideoViewController,
),
key: ValueKey(
'remote_video_$currentRemoteUid',
), // 使 key remoteUid
child: AgoraVideoView(controller: remoteVideoViewController),
); );
} else { } else {
print('⚠️ [VideoCallPage] engine 为 null,无法显示远端视频'); print('⚠️ [VideoCallPage] engine 为 null,无法显示远端视频');
@ -342,25 +361,76 @@ class _VideoCallPageState extends State<VideoCallPage> {
} else { } else {
print('⚠️ [VideoCallPage] currentRemoteUid 为 null,无法显示远端视频'); print('⚠️ [VideoCallPage] currentRemoteUid 为 null,无法显示远端视频');
} }
// //
if (_localVideoViewController != null) { if (_localVideoViewController != null) {
print('📞 [VideoCallPage] 显示本地视频视图'); print('📞 [VideoCallPage] 显示本地视频视图');
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
height: 1.sh, height: 1.sh,
child: AgoraVideoView(
controller: _localVideoViewController!,
),
child: AgoraVideoView(controller: _localVideoViewController!),
); );
} }
// //
print('📞 [VideoCallPage] 显示头像背景'); print('📞 [VideoCallPage] 显示头像背景');
return _buildAvatarBackground(); return _buildAvatarBackground();
}); });
} }
///
Widget _buildLocalVideoPreview() {
return Obx(() {
// Obx 访
final callSession = _callController.currentCall.value;
final isVideoCall =
callSession != null && callSession.callType == CallType.video;
final callDuration = _callController.callDurationSeconds.value;
final isCallConnected = callSession != null && callDuration > 0;
//
if (isVideoCall && _localVideoViewController == null) {
final engine = _rtcManager.engine;
if (engine != null) {
_localVideoViewController = VideoViewController(
rtcEngine: engine,
canvas: const VideoCanvas(uid: 0),
);
print('📞 [VideoCallPage] 在 _buildLocalVideoPreview 中初始化本地视频视图');
}
}
//
if (!isVideoCall ||
!isCallConnected ||
_localVideoViewController == null) {
return const SizedBox.shrink();
}
return Positioned(
top: 26.w,
right: 26.w,
child: Container(
width: 120.w,
height: 160.w, // 4:3
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.w),
border: Border.all(color: Colors.white.withOpacity(0.3), width: 1),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
clipBehavior: Clip.antiAlias,
child: AgoraVideoView(controller: _localVideoViewController!),
),
);
});
}
/// ///
Widget _buildAvatarBackground() { Widget _buildAvatarBackground() {
return SizedBox( return SizedBox(
@ -383,9 +453,7 @@ class _VideoCallPageState extends State<VideoCallPage> {
/// ///
Widget _buildDefaultBackground() { Widget _buildDefaultBackground() {
return Container(
color: Colors.black,
);
return Container(color: Colors.black);
} }
/// ///
@ -395,14 +463,19 @@ class _VideoCallPageState extends State<VideoCallPage> {
final callSession = _callController.currentCall.value; final callSession = _callController.currentCall.value;
final callDuration = _callController.callDurationSeconds.value; final callDuration = _callController.callDurationSeconds.value;
final isCallConnected = callSession != null && callDuration > 0; final isCallConnected = callSession != null && callDuration > 0;
print('📞 [VideoCallPage] _buildUserInfo Obx 重建,isCallConnected: $isCallConnected, callDuration: $callDuration');
//
if (isCallConnected) {
final isVideoCall =
callSession != null && callSession.callType == CallType.video;
print(
'📞 [VideoCallPage] _buildUserInfo Obx 重建,isCallConnected: $isCallConnected, callDuration: $callDuration, isVideoCall: $isVideoCall',
);
//
// 使
if (isCallConnected && isVideoCall) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
return Positioned( return Positioned(
top: MediaQuery.of(context).size.height * 0.15, top: MediaQuery.of(context).size.height * 0.15,
left: 0, left: 0,
@ -454,12 +527,12 @@ class _VideoCallPageState extends State<VideoCallPage> {
final callSession = _callController.currentCall.value; final callSession = _callController.currentCall.value;
final callDuration = _callController.callDurationSeconds.value; final callDuration = _callController.callDurationSeconds.value;
final isCallConnected = callSession != null && callDuration > 0; final isCallConnected = callSession != null && callDuration > 0;
// //
if (!widget.isInitiator && !isCallConnected) { if (!widget.isInitiator && !isCallConnected) {
final isVideoCall = widget.callType == 'video'; final isVideoCall = widget.callType == 'video';
final inviteText = isVideoCall ? '邀请你视频通话' : '邀请你语音通话'; final inviteText = isVideoCall ? '邀请你视频通话' : '邀请你语音通话';
return Positioned( return Positioned(
bottom: MediaQuery.of(context).size.height * 0.25, bottom: MediaQuery.of(context).size.height * 0.25,
left: 0, left: 0,
@ -476,12 +549,12 @@ class _VideoCallPageState extends State<VideoCallPage> {
), ),
); );
} }
// //
if (isCallConnected && !showControls.value) { if (isCallConnected && !showControls.value) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
// "正在呼叫中" // "正在呼叫中"
final duration = Duration(seconds: callDuration); final duration = Duration(seconds: callDuration);
return Positioned( return Positioned(
@ -509,7 +582,7 @@ class _VideoCallPageState extends State<VideoCallPage> {
final callSession = _callController.currentCall.value; final callSession = _callController.currentCall.value;
final callDuration = _callController.callDurationSeconds.value; final callDuration = _callController.callDurationSeconds.value;
final isCallConnected = callSession != null && callDuration > 0; final isCallConnected = callSession != null && callDuration > 0;
// "拒绝""接听" // "拒绝""接听"
if (!widget.isInitiator && !isCallConnected) { if (!widget.isInitiator && !isCallConnected) {
return Positioned( return Positioned(
@ -539,12 +612,12 @@ class _VideoCallPageState extends State<VideoCallPage> {
), ),
); );
} }
// //
if (isCallConnected && !showControls.value) { if (isCallConnected && !showControls.value) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
// //
return Positioned( return Positioned(
bottom: 40.h, bottom: 40.h,
@ -597,11 +670,9 @@ class _VideoCallPageState extends State<VideoCallPage> {
} else if (isAccept) { } else if (isAccept) {
buttonColor = Color(0xFF34C759); // 绿 buttonColor = Color(0xFF34C759); // 绿
} else { } else {
buttonColor = isActive
? Colors.white.withOpacity(0.3)
: Colors.white.withOpacity(0.2);
buttonColor = isActive ? Colors.white : Colors.white.withOpacity(0.2);
} }
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
child: Column( child: Column(
@ -615,23 +686,22 @@ class _VideoCallPageState extends State<VideoCallPage> {
), ),
child: Icon( child: Icon(
icon, icon,
color: Colors.white,
color: isActive && icon != Icons.call_end && icon != Icons.phone
? Colors.black
: Colors.white,
size: 28.w, size: 28.w,
), ),
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
label, label,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
style: TextStyle(color: Colors.white, fontSize: 12.sp),
), ),
], ],
), ),
); );
} }
/// ///
Future<void> _acceptCall() async { Future<void> _acceptCall() async {
if (widget.callMessage == null) { if (widget.callMessage == null) {
@ -645,20 +715,21 @@ class _VideoCallPageState extends State<VideoCallPage> {
if (Get.isRegistered<ChatController>(tag: tag)) { if (Get.isRegistered<ChatController>(tag: tag)) {
chatController = Get.find<ChatController>(tag: tag); chatController = Get.find<ChatController>(tag: tag);
} }
final accepted = await _callController.acceptCall( final accepted = await _callController.acceptCall(
message: widget.callMessage!, message: widget.callMessage!,
chatController: chatController, chatController: chatController,
); );
if (accepted) { if (accepted) {
// UI会自动更新
print('✅ [VideoCallPage] 通话已接通');
//
_initLocalVideo();
print('✅ [VideoCallPage] 通话已接通,已重新初始化本地视频视图');
} else { } else {
SmartDialog.showToast('接听失败'); SmartDialog.showToast('接听失败');
} }
} }
/// ///
Future<void> _rejectCall() async { Future<void> _rejectCall() async {
// ChatController // ChatController
@ -667,16 +738,15 @@ class _VideoCallPageState extends State<VideoCallPage> {
if (Get.isRegistered<ChatController>(tag: tag)) { if (Get.isRegistered<ChatController>(tag: tag)) {
chatController = Get.find<ChatController>(tag: tag); chatController = Get.find<ChatController>(tag: tag);
} }
final rejected = await _callController.rejectCall( final rejected = await _callController.rejectCall(
message: widget.callMessage!, message: widget.callMessage!,
chatController: chatController, chatController: chatController,
); );
if (rejected) { if (rejected) {
// //
Get.back(); Get.back();
} }
} }
} }
Loading…
Cancel
Save