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.
 
 
 
 
 

195 lines
6.1 KiB

import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/controller/message/call_controller.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/pages/message/video_call_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
/// 视频通话小窗组件
class VideoCallOverlayWidget extends StatefulWidget {
final VoidCallback? onClose;
final String targetUserId;
final String? targetUserName;
final String? targetAvatarUrl;
const VideoCallOverlayWidget({
super.key,
this.onClose,
required this.targetUserId,
this.targetUserName,
this.targetAvatarUrl,
});
@override
State<VideoCallOverlayWidget> createState() => _VideoCallOverlayWidgetState();
}
class _VideoCallOverlayWidgetState extends State<VideoCallOverlayWidget> {
Offset _position = Offset.zero;
bool _isDragging = false;
final CallController _callController = CallController.instance;
@override
void initState() {
super.initState();
// 初始位置设置为右上角
WidgetsBinding.instance.addPostFrameCallback((_) {
final size = MediaQuery.of(context).size;
setState(() {
_position = Offset(
size.width - 100.w,
100,
);
});
});
}
/// 吸附到边缘
void _snapToEdge(double screenWidth) {
final centerX = screenWidth / 2;
final targetX = _position.dx < centerX
? 0.0
: screenWidth - 100.w;
setState(() {
_position = Offset(targetX, _position.dy);
_isDragging = false;
});
}
/// 格式化通话时长
String _formatDuration(int seconds) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = twoDigits(seconds ~/ 60);
final secs = twoDigits(seconds % 60);
return '$minutes:$secs';
}
@override
Widget build(BuildContext context) {
final screenSize = MediaQuery.of(context).size;
return AnimatedPositioned(
duration: _isDragging
? const Duration(milliseconds: 50)
: const Duration(milliseconds: 300),
curve: _isDragging ? Curves.linear : Curves.easeOut,
left: _position.dx,
top: _position.dy,
child: _buildContent(screenSize),
);
}
Widget _buildContent(Size screenSize) {
return GestureDetector(
onPanStart: (details) {
setState(() {
_isDragging = true;
});
},
onPanUpdate: (details) {
setState(() {
_position += details.delta;
_position = Offset(
_position.dx.clamp(0.0, screenSize.width - 100.w),
_position.dy.clamp(0.0, screenSize.height - 100.w),
);
});
},
onPanEnd: (details) {
_snapToEdge(screenSize.width);
},
child: Obx(() {
final callSession = _callController.currentCall.value;
final isConnected = callSession != null && _callController.callDurationSeconds.value > 0;
final callDuration = _callController.callDurationSeconds.value;
return Container(
width: 100.w,
height: 100.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.w),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: Stack(
children: [
// 背景:头像
Container(
width: 100.w,
height: 100.w,
color: Colors.black,
child: widget.targetAvatarUrl != null && widget.targetAvatarUrl!.isNotEmpty
? CachedNetworkImage(
imageUrl: widget.targetAvatarUrl!,
fit: BoxFit.cover,
errorWidget: (context, url, error) => Image.asset(
Assets.imagesUserAvatar,
fit: BoxFit.cover,
),
)
: Image.asset(
Assets.imagesUserAvatar,
fit: BoxFit.cover,
),
),
// 半透明遮罩
Container(
color: Colors.black.withOpacity(0.4),
),
// 内容:昵称和状态
Positioned(
bottom: 8.w,
left: 4.w,
right: 4.w,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 昵称
Text(
widget.targetUserName ?? widget.targetUserId,
style: TextStyle(
color: Colors.white,
fontSize: 10.sp,
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 2.h),
// 通话状态
Text(
isConnected ? _formatDuration(callDuration) : '通话中',
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 8.sp,
),
),
],
),
),
],
),
),
).onTap(() {
// 点击小窗,返回视频通话页面
Get.to(() => VideoCallPage(
targetUserId: widget.targetUserId,
isInitiator: callSession?.isInitiator ?? true,
));
widget.onClose?.call();
});
}),
);
}
}