You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
752 lines
23 KiB
752 lines
23 KiB
import 'dart:async';
|
|
import 'dart:ui';
|
|
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:dating_touchme_app/generated/assets.dart';
|
|
import 'package:dating_touchme_app/rtc/rtc_manager.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
|
|
import '../../controller/message/call_controller.dart';
|
|
import '../../controller/message/chat_controller.dart';
|
|
import '../../controller/message/conversation_controller.dart';
|
|
import '../../controller/overlay_controller.dart';
|
|
import '../../model/home/marriage_data.dart';
|
|
|
|
/// 视频通话页面
|
|
class VideoCallPage extends StatefulWidget {
|
|
final String targetUserId;
|
|
final String? callType;
|
|
final String? channelId;
|
|
final MarriageData? userData;
|
|
final bool isInitiator; // 是否是发起方
|
|
final EMMessage? callMessage; // 通话消息(用于接听通话时更新消息状态)
|
|
|
|
const VideoCallPage({
|
|
super.key,
|
|
required this.targetUserId,
|
|
this.callType,
|
|
this.channelId,
|
|
this.userData,
|
|
this.isInitiator = true,
|
|
this.callMessage,
|
|
});
|
|
|
|
@override
|
|
State<VideoCallPage> createState() => _VideoCallPageState();
|
|
}
|
|
|
|
class _VideoCallPageState extends State<VideoCallPage> {
|
|
final CallController _callController = CallController.instance;
|
|
final RTCManager _rtcManager = RTCManager.instance;
|
|
|
|
Timer? _durationTimer;
|
|
|
|
String? _targetUserName;
|
|
String? _targetAvatarUrl;
|
|
|
|
// 是否显示控制按钮和时长(接通后5秒隐藏)
|
|
final RxBool showControls = true.obs;
|
|
Timer? _hideControlsTimer;
|
|
|
|
// 本地视频视图控制器
|
|
VideoViewController? _localVideoViewController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadUserInfo();
|
|
_initCallStatus();
|
|
_startDurationTimer();
|
|
_initLocalVideo();
|
|
|
|
// 设置系统UI样式
|
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
|
}
|
|
|
|
/// 初始化本地视频视图
|
|
void _initLocalVideo() {
|
|
final callSession = _callController.currentCall.value;
|
|
// 如果是视频通话,创建本地视频视图控制器
|
|
if (callSession != null && callSession.callType == CallType.video) {
|
|
final engine = _rtcManager.engine;
|
|
if (engine != null) {
|
|
_localVideoViewController = VideoViewController(
|
|
rtcEngine: engine,
|
|
canvas: const VideoCanvas(uid: 0),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 初始化通话状态
|
|
void _initCallStatus() {
|
|
// 不需要初始化,直接使用 CallController 的响应式变量
|
|
}
|
|
|
|
/// 判断通话是否已接通
|
|
bool get _isCallConnected {
|
|
final callSession = _callController.currentCall.value;
|
|
return callSession != null && _callController.callDurationSeconds.value > 0;
|
|
}
|
|
|
|
/// 加载用户信息
|
|
Future<void> _loadUserInfo() async {
|
|
// 优先使用传入的 userData
|
|
if (widget.userData != null) {
|
|
setState(() {
|
|
_targetUserName = widget.userData!.nickName.isNotEmpty
|
|
? widget.userData!.nickName
|
|
: widget.targetUserId;
|
|
_targetAvatarUrl = widget.userData!.profilePhoto;
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 如果没有传入 userData,尝试从 ConversationController 获取
|
|
if (Get.isRegistered<ConversationController>()) {
|
|
final conversationController = Get.find<ConversationController>();
|
|
|
|
// 先从缓存中获取
|
|
final cachedUserInfo = conversationController.getCachedUserInfo(
|
|
widget.targetUserId,
|
|
);
|
|
if (cachedUserInfo != null &&
|
|
(cachedUserInfo.nickName != null ||
|
|
cachedUserInfo.avatarUrl != null)) {
|
|
setState(() {
|
|
_targetUserName = cachedUserInfo.nickName ?? widget.targetUserId;
|
|
_targetAvatarUrl = cachedUserInfo.avatarUrl;
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 如果缓存中没有,尝试从 IM 加载
|
|
final userInfo = await conversationController.loadContact(
|
|
widget.targetUserId,
|
|
);
|
|
if (userInfo != null &&
|
|
(userInfo.nickName != null || userInfo.avatarUrl != null)) {
|
|
setState(() {
|
|
_targetUserName = userInfo.nickName ?? widget.targetUserId;
|
|
_targetAvatarUrl = userInfo.avatarUrl;
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 如果都获取不到,使用默认值
|
|
setState(() {
|
|
_targetUserName = widget.targetUserId;
|
|
_targetAvatarUrl = null;
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_durationTimer?.cancel();
|
|
_hideControlsTimer?.cancel();
|
|
_localVideoViewController?.dispose();
|
|
SystemChrome.setEnabledSystemUIMode(
|
|
SystemUiMode.manual,
|
|
overlays: SystemUiOverlay.values,
|
|
);
|
|
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
|
|
super.dispose();
|
|
}
|
|
|
|
/// 开始通话时长计时器
|
|
void _startDurationTimer() {
|
|
// 监听 CallController 的通话状态变化
|
|
_callController.currentCall.listen((callSession) {
|
|
if (mounted) {
|
|
final wasConnected = _isCallConnected;
|
|
// 如果通话存在且已经开始计时,说明已接通
|
|
if (callSession != null &&
|
|
_callController.callDurationSeconds.value > 0) {
|
|
if (!wasConnected) {
|
|
// 刚接通,启动5秒隐藏定时器
|
|
_startHideControlsTimer();
|
|
}
|
|
} else if (callSession == null) {
|
|
_hideControlsTimer?.cancel();
|
|
showControls.value = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
// 监听通话时长变化(已接通时启动隐藏定时器)
|
|
_callController.callDurationSeconds.listen((seconds) {
|
|
if (mounted && !_isCallConnected && seconds > 0) {
|
|
// 如果时长开始增加,说明刚接通
|
|
_startHideControlsTimer();
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 启动隐藏控制按钮的定时器(5秒后隐藏)
|
|
void _startHideControlsTimer() {
|
|
_hideControlsTimer?.cancel();
|
|
showControls.value = true;
|
|
_hideControlsTimer = Timer(Duration(seconds: 5), () {
|
|
if (mounted && _isCallConnected) {
|
|
showControls.value = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 切换控制按钮的显示/隐藏(点击屏幕时调用)
|
|
void _toggleControlsVisibility() {
|
|
if (!_isCallConnected) return; // 未接通时不处理
|
|
|
|
showControls.value = !showControls.value;
|
|
|
|
// 如果显示控制按钮,重新启动5秒隐藏定时器
|
|
if (showControls.value) {
|
|
_startHideControlsTimer();
|
|
} else {
|
|
_hideControlsTimer?.cancel();
|
|
}
|
|
}
|
|
|
|
/// 格式化通话时长
|
|
String _formatDuration(Duration duration) {
|
|
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
|
final minutes = twoDigits(duration.inMinutes.remainder(60));
|
|
final seconds = twoDigits(duration.inSeconds.remainder(60));
|
|
return '$minutes:$seconds';
|
|
}
|
|
|
|
/// 切换麦克风状态
|
|
void _toggleMic() {
|
|
_callController.toggleMic();
|
|
}
|
|
|
|
/// 切换扬声器状态
|
|
void _toggleSpeaker() {
|
|
_callController.toggleSpeaker();
|
|
}
|
|
|
|
/// 挂断通话
|
|
void _hangUp() async {
|
|
await _callController.hangUpCall();
|
|
// 返回上一页
|
|
Get.back();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return PopScope(
|
|
canPop: false, // 禁止手势返回
|
|
onPopInvoked: (didPop) {
|
|
// 已经禁止返回,所以这里不会被调用
|
|
// 如果需要返回,应该通过挂断按钮或其他明确的操作
|
|
},
|
|
child: Scaffold(
|
|
backgroundColor: Colors.black,
|
|
body: GestureDetector(
|
|
onTap: _toggleControlsVisibility,
|
|
child: Stack(
|
|
children: [
|
|
// 背景视频/头像(模糊)
|
|
_buildBackground(),
|
|
|
|
// 最小化按钮(左上角)
|
|
_buildMinimizeButton(),
|
|
|
|
// 本地视频小窗口(右上角,视频通话且已接通时显示)
|
|
_buildLocalVideoPreview(),
|
|
|
|
// 用户信息
|
|
_buildUserInfo(),
|
|
|
|
// 通话时长
|
|
_buildCallDuration(),
|
|
|
|
// 底部控制按钮
|
|
_buildControlButtons(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 构建最小化按钮
|
|
Widget _buildMinimizeButton() {
|
|
return Positioned(
|
|
top: 26.w,
|
|
left: 26.w,
|
|
child: GestureDetector(
|
|
onTap: _minimizeCall,
|
|
child: Image.asset(Assets.imagesCloseArrow, width: 20.w, height: 20.w),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 最小化通话
|
|
void _minimizeCall() {
|
|
// 显示视频通话小窗
|
|
if (Get.isRegistered<OverlayController>()) {
|
|
final overlayController = Get.find<OverlayController>();
|
|
overlayController.showVideoCall(
|
|
targetUserId: widget.targetUserId,
|
|
targetUserName: _targetUserName,
|
|
targetAvatarUrl: _targetAvatarUrl,
|
|
);
|
|
}
|
|
// 返回上一页
|
|
Get.back();
|
|
}
|
|
|
|
/// 构建背景
|
|
Widget _buildBackground() {
|
|
// 使用 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: remoteVideoViewController),
|
|
);
|
|
} else {
|
|
print('⚠️ [VideoCallPage] engine 为 null,无法显示远端视频');
|
|
}
|
|
} 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();
|
|
});
|
|
}
|
|
|
|
/// 构建本地视频预览小窗口(右上角)
|
|
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() {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
height: 1.sh,
|
|
child: _targetAvatarUrl != null && _targetAvatarUrl!.isNotEmpty
|
|
? ImageFiltered(
|
|
imageFilter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
|
|
child: CachedNetworkImage(
|
|
imageUrl: _targetAvatarUrl!,
|
|
fit: BoxFit.cover,
|
|
width: double.infinity,
|
|
height: double.infinity,
|
|
errorWidget: (context, url, error) => _buildDefaultBackground(),
|
|
),
|
|
)
|
|
: _buildDefaultBackground(),
|
|
);
|
|
}
|
|
|
|
/// 构建默认背景
|
|
Widget _buildDefaultBackground() {
|
|
return Container(color: Colors.black);
|
|
}
|
|
|
|
/// 构建用户信息
|
|
Widget _buildUserInfo() {
|
|
return Obx(() {
|
|
// 在 Obx 中直接访问响应式变量,确保建立监听关系
|
|
final callSession = _callController.currentCall.value;
|
|
final callDuration = _callController.callDurationSeconds.value;
|
|
final isCallConnected = callSession != null && callDuration > 0;
|
|
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 Positioned(
|
|
top: MediaQuery.of(context).size.height * 0.15,
|
|
left: 0,
|
|
right: 0,
|
|
child: Column(
|
|
children: [
|
|
// 头像
|
|
ClipOval(
|
|
child: _targetAvatarUrl != null && _targetAvatarUrl!.isNotEmpty
|
|
? CachedNetworkImage(
|
|
imageUrl: _targetAvatarUrl!,
|
|
width: 120.w,
|
|
height: 120.w,
|
|
fit: BoxFit.cover,
|
|
errorWidget: (context, url, error) => Image.asset(
|
|
Assets.imagesUserAvatar,
|
|
width: 120.w,
|
|
height: 120.w,
|
|
fit: BoxFit.cover,
|
|
),
|
|
)
|
|
: Image.asset(
|
|
Assets.imagesUserAvatar,
|
|
width: 120.w,
|
|
height: 120.w,
|
|
fit: BoxFit.cover,
|
|
),
|
|
),
|
|
SizedBox(height: 16.h),
|
|
// 用户名
|
|
Text(
|
|
_targetUserName ?? widget.targetUserId,
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 24.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
});
|
|
}
|
|
|
|
/// 构建通话时长/状态文本
|
|
Widget _buildCallDuration() {
|
|
return Obx(() {
|
|
// 在 Obx 中直接访问响应式变量,确保建立监听关系
|
|
final callSession = _callController.currentCall.value;
|
|
final callDuration = _callController.callDurationSeconds.value;
|
|
final isCallConnected = callSession != null && callDuration > 0;
|
|
|
|
// 如果是被呼叫方且未接通,显示邀请文字
|
|
if (!widget.isInitiator && !isCallConnected) {
|
|
final isVideoCall = widget.callType == 'video';
|
|
final inviteText = isVideoCall ? '邀请你视频通话' : '邀请你语音通话';
|
|
|
|
return Positioned(
|
|
bottom: MediaQuery.of(context).size.height * 0.25,
|
|
left: 0,
|
|
right: 0,
|
|
child: Center(
|
|
child: Text(
|
|
inviteText,
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// 如果已接通但控制按钮已隐藏,不显示时长
|
|
if (isCallConnected && !showControls.value) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
// 呼叫方或已接通,显示时长或"正在呼叫中"
|
|
final duration = Duration(seconds: callDuration);
|
|
return Positioned(
|
|
bottom: MediaQuery.of(context).size.height * 0.25,
|
|
left: 0,
|
|
right: 0,
|
|
child: Center(
|
|
child: Text(
|
|
isCallConnected ? _formatDuration(duration) : '正在呼叫中',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
});
|
|
}
|
|
|
|
/// 构建控制按钮
|
|
Widget _buildControlButtons() {
|
|
return Obx(() {
|
|
// 在 Obx 中直接访问响应式变量,确保建立监听关系
|
|
final callSession = _callController.currentCall.value;
|
|
final callDuration = _callController.callDurationSeconds.value;
|
|
final isCallConnected = callSession != null && callDuration > 0;
|
|
|
|
// 如果是被呼叫方且未接通,显示"拒绝"和"接听"按钮
|
|
if (!widget.isInitiator && !isCallConnected) {
|
|
return Positioned(
|
|
bottom: 40.h,
|
|
left: 0,
|
|
right: 0,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
// 拒绝按钮
|
|
_buildControlButton(
|
|
icon: Icons.call_end,
|
|
label: '拒绝',
|
|
isActive: true,
|
|
onTap: _rejectCall,
|
|
isReject: true,
|
|
),
|
|
// 接听按钮
|
|
_buildControlButton(
|
|
icon: Icons.phone,
|
|
label: '接听',
|
|
isActive: true,
|
|
onTap: _acceptCall,
|
|
isAccept: true,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// 如果已接通但控制按钮已隐藏,不显示按钮
|
|
if (isCallConnected && !showControls.value) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
// 呼叫方或已接通,显示常规控制按钮
|
|
return Positioned(
|
|
bottom: 40.h,
|
|
left: 0,
|
|
right: 0,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
// 免提按钮
|
|
_buildControlButton(
|
|
icon: Icons.volume_up,
|
|
label: '免提',
|
|
isActive: _callController.isSpeakerOn.value,
|
|
onTap: _toggleSpeaker,
|
|
),
|
|
// 麦克风按钮
|
|
_buildControlButton(
|
|
icon: Icons.mic,
|
|
label: '麦克风',
|
|
isActive: !_callController.isMicMuted.value,
|
|
onTap: _toggleMic,
|
|
),
|
|
// 挂断按钮
|
|
_buildControlButton(
|
|
icon: Icons.call_end,
|
|
label: '挂断',
|
|
isActive: true,
|
|
onTap: _hangUp,
|
|
isHangUp: true,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
});
|
|
}
|
|
|
|
/// 构建控制按钮
|
|
Widget _buildControlButton({
|
|
required IconData icon,
|
|
required String label,
|
|
required bool isActive,
|
|
required VoidCallback onTap,
|
|
bool isHangUp = false,
|
|
bool isReject = false,
|
|
bool isAccept = false,
|
|
}) {
|
|
Color buttonColor;
|
|
if (isHangUp || isReject) {
|
|
buttonColor = Color(0xFFFF3B30); // 红色
|
|
} else if (isAccept) {
|
|
buttonColor = Color(0xFF34C759); // 绿色
|
|
} else {
|
|
buttonColor = isActive ? Colors.white : Colors.white.withOpacity(0.2);
|
|
}
|
|
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
width: 56.w,
|
|
height: 56.w,
|
|
decoration: BoxDecoration(
|
|
color: buttonColor,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: isActive && icon != Icons.call_end && icon != Icons.phone
|
|
? Colors.black
|
|
: Colors.white,
|
|
size: 28.w,
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
label,
|
|
style: TextStyle(color: Colors.white, fontSize: 12.sp),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 接听通话
|
|
Future<void> _acceptCall() async {
|
|
if (widget.callMessage == null) {
|
|
SmartDialog.showToast('未找到通话邀请消息');
|
|
return;
|
|
}
|
|
|
|
// 尝试获取 ChatController
|
|
ChatController? chatController;
|
|
final tag = 'chat_${widget.targetUserId}';
|
|
if (Get.isRegistered<ChatController>(tag: tag)) {
|
|
chatController = Get.find<ChatController>(tag: tag);
|
|
}
|
|
|
|
final accepted = await _callController.acceptCall(
|
|
message: widget.callMessage!,
|
|
chatController: chatController,
|
|
);
|
|
|
|
if (accepted) {
|
|
// 通话已接通,重新初始化本地视频视图(接收方接听后需要初始化)
|
|
_initLocalVideo();
|
|
print('✅ [VideoCallPage] 通话已接通,已重新初始化本地视频视图');
|
|
} else {
|
|
SmartDialog.showToast('接听失败');
|
|
}
|
|
}
|
|
|
|
/// 拒绝通话
|
|
Future<void> _rejectCall() async {
|
|
// 尝试获取 ChatController
|
|
ChatController? chatController;
|
|
final tag = 'chat_${widget.targetUserId}';
|
|
if (Get.isRegistered<ChatController>(tag: tag)) {
|
|
chatController = Get.find<ChatController>(tag: tag);
|
|
}
|
|
|
|
final rejected = await _callController.rejectCall(
|
|
message: widget.callMessage!,
|
|
chatController: chatController,
|
|
);
|
|
|
|
if (rejected) {
|
|
// 拒绝成功,返回上一页
|
|
Get.back();
|
|
}
|
|
}
|
|
}
|