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.
 
 
 
 
 

465 lines
13 KiB

import 'dart:async';
import 'dart:ui';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../controller/message/call_manager.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 MarriageData? userData;
final bool isInitiator; // 是否是发起方
const VideoCallPage({
super.key,
required this.targetUserId,
this.userData,
this.isInitiator = true,
});
@override
State<VideoCallPage> createState() => _VideoCallPageState();
}
class _VideoCallPageState extends State<VideoCallPage> {
final CallManager _callManager = CallManager.instance;
bool _isMicMuted = false;
bool _isSpeakerOn = false;
Duration _callDuration = Duration.zero;
Timer? _durationTimer;
String? _targetUserName;
String? _targetAvatarUrl;
// 通话是否已接通
bool _isCallConnected = false;
@override
void initState() {
super.initState();
_initializeCall();
_loadUserInfo();
_initCallStatus();
_startDurationTimer();
// 设置系统UI样式
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
}
/// 初始化通话状态
void _initCallStatus() {
// 检查当前通话状态
final callSession = _callManager.currentCall.value;
if (callSession != null && _callManager.callDurationSeconds.value > 0) {
// 如果通话已存在且已经开始计时,说明已接通
_isCallConnected = true;
_callDuration = Duration(seconds: _callManager.callDurationSeconds.value);
} else {
// 否则是未接通状态
_isCallConnected = false;
}
}
/// 加载用户信息
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 获取
try {
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;
}
}
} catch (e) {
print('⚠️ [VideoCallPage] 加载用户信息失败: $e');
}
// 如果都获取不到,使用默认值
setState(() {
_targetUserName = widget.targetUserId;
_targetAvatarUrl = null;
});
}
@override
void dispose() {
_durationTimer?.cancel();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
super.dispose();
}
/// 初始化通话
Future<void> _initializeCall() async {
try {
// TODO: 初始化RTC Engine并加入频道
// await _rtcManager.initialize(appId: 'your_app_id');
// await _rtcManager.joinChannel(token: 'token', channelId: 'channel_id', uid: uid);
} catch (e) {
print('初始化通话失败: $e');
}
}
/// 开始通话时长计时器
void _startDurationTimer() {
// 监听 CallManager 的通话状态变化
_callManager.currentCall.listen((callSession) {
if (mounted) {
final wasConnected = _isCallConnected;
// 如果通话存在且已经开始计时,说明已接通
if (callSession != null && _callManager.callDurationSeconds.value > 0) {
_isCallConnected = true;
if (!wasConnected) {
// 刚接通,同步时长
_callDuration = Duration(seconds: _callManager.callDurationSeconds.value);
}
} else if (callSession == null) {
_isCallConnected = false;
}
setState(() {});
}
});
// 监听通话时长变化(已接通时更新)
_callManager.callDurationSeconds.listen((seconds) {
if (mounted && _isCallConnected) {
setState(() {
_callDuration = Duration(seconds: seconds);
});
} else if (mounted && !_isCallConnected && seconds > 0) {
// 如果时长开始增加,说明刚接通
setState(() {
_isCallConnected = true;
_callDuration = Duration(seconds: seconds);
});
}
});
// 如果未接通,使用本地计时器检查状态变化
if (!_isCallConnected) {
_durationTimer = Timer.periodic(Duration(seconds: 1), (timer) {
if (mounted) {
final callSession = _callManager.currentCall.value;
final duration = _callManager.callDurationSeconds.value;
// 检查是否已接通(通话存在且时长大于0)
if (callSession != null && duration > 0) {
_isCallConnected = true;
_callDuration = Duration(seconds: duration);
timer.cancel();
setState(() {});
}
}
});
}
}
/// 格式化通话时长
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() {
setState(() {
_isMicMuted = !_isMicMuted;
});
// TODO: 调用RTC Manager切换麦克风
// _rtcManager.enableAudio(!_isMicMuted);
}
/// 切换扬声器状态
void _toggleSpeaker() {
setState(() {
_isSpeakerOn = !_isSpeakerOn;
});
// TODO: 调用RTC Manager切换扬声器
// _rtcManager.setEnableSpeakerphone(_isSpeakerOn);
}
/// 挂断通话
void _hangUp() async {
try {
// TODO: 离开RTC频道
// await _rtcManager.leaveChannel();
// 结束通话(传递通话时长)
await _callManager.endCall(callDuration: _callDuration.inSeconds);
// 返回上一页
Get.back();
} catch (e) {
print('挂断通话失败: $e');
Get.back();
}
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false, // 禁止手势返回
onPopInvoked: (didPop) {
// 已经禁止返回,所以这里不会被调用
// 如果需要返回,应该通过挂断按钮或其他明确的操作
},
child: Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
// 背景视频/头像(模糊)
_buildBackground(),
// 最小化按钮(左上角)
_buildMinimizeButton(),
// 用户信息
_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() {
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 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 Positioned(
bottom: MediaQuery.of(context).size.height * 0.25,
left: 0,
right: 0,
child: Center(
child: Text(
_isCallConnected ? _formatDuration(_callDuration) : '正在呼叫中',
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
),
);
}
/// 构建控制按钮
Widget _buildControlButtons() {
return Positioned(
bottom: 40.h,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// 免提按钮
_buildControlButton(
icon: Icons.volume_up,
label: '免提',
isActive: _isSpeakerOn,
onTap: _toggleSpeaker,
),
// 麦克风按钮
_buildControlButton(
icon: Icons.mic,
label: '麦克风',
isActive: !_isMicMuted,
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,
}) {
return GestureDetector(
onTap: onTap,
child: Column(
children: [
Container(
width: 56.w,
height: 56.w,
decoration: BoxDecoration(
color: isHangUp
? Color(0xFFFF3B30)
: (isActive ? Colors.white.withOpacity(0.3) : Colors.white.withOpacity(0.2)),
shape: BoxShape.circle,
),
child: Icon(
icon,
color: Colors.white,
size: 28.w,
),
),
SizedBox(height: 8.h),
Text(
label,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
),
],
),
);
}
}