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.
192 lines
5.9 KiB
192 lines
5.9 KiB
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
|
import 'package:dating_touchme_app/controller/discover/room_controller.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:dating_touchme_app/rtc/rtc_manager.dart';
|
|
|
|
/// 可拖拽的 Overlay 小组件
|
|
class DraggableOverlayWidget extends StatefulWidget {
|
|
final VoidCallback? onClose;
|
|
final Widget? child;
|
|
final double size;
|
|
final Color backgroundColor;
|
|
|
|
const DraggableOverlayWidget({
|
|
super.key,
|
|
this.onClose,
|
|
this.child,
|
|
this.size = 60,
|
|
this.backgroundColor = const Color.fromRGBO(0, 0, 0, 0.5),
|
|
});
|
|
|
|
@override
|
|
State<DraggableOverlayWidget> createState() => _DraggableOverlayWidgetState();
|
|
}
|
|
|
|
class _DraggableOverlayWidgetState extends State<DraggableOverlayWidget> {
|
|
Offset _position = Offset.zero;
|
|
bool _isDragging = false;
|
|
final RTCManager _rtcManager = RTCManager.instance;
|
|
final RoomController _roomController = Get.find<RoomController>();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// 初始位置设置为右上角
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
final size = MediaQuery.of(context).size;
|
|
setState(() {
|
|
_position = Offset(
|
|
size.width - 100.w, // 右上角,无间隙(使用100.w因为用户改了size)
|
|
100,
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
/// 吸附到边缘
|
|
void _snapToEdge(double screenWidth) {
|
|
final centerX = screenWidth / 2;
|
|
final targetX = _position.dx < centerX
|
|
? 0.0 // 吸附到左边,无间隙
|
|
: screenWidth - 100.w; // 吸附到右边,无间隙(使用100.w因为用户改了size)
|
|
|
|
// 使用 setState 更新位置,AnimatedPositioned 会自动处理动画
|
|
setState(() {
|
|
_position = Offset(targetX, _position.dy);
|
|
_isDragging = false;
|
|
});
|
|
}
|
|
|
|
/// 构建主播视频视图
|
|
Widget _buildAnchorVideo() {
|
|
final engine = _rtcManager.engine;
|
|
|
|
if (_roomController.rtcChannelDetail.value?.anchorInfo == null ||
|
|
engine == null) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
|
color: Colors.grey.withOpacity(0.5),
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
'等待主播',
|
|
style: TextStyle(
|
|
color: Colors.white.withOpacity(0.8),
|
|
fontSize: 12.w,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
|
child: _roomController.currentRole == CurrentRole.broadcaster
|
|
? AgoraVideoView(
|
|
controller: VideoViewController(
|
|
rtcEngine: engine,
|
|
canvas: const VideoCanvas(uid: 0),
|
|
),
|
|
)
|
|
: AgoraVideoView(
|
|
controller: VideoViewController.remote(
|
|
rtcEngine: engine,
|
|
canvas: VideoCanvas(
|
|
uid: _roomController.rtcChannelDetail.value?.anchorInfo?.uid,
|
|
),
|
|
connection: RtcConnection(
|
|
channelId: _rtcManager.currentChannelId ?? '',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@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;
|
|
// 限制在屏幕范围内(使用100.w因为用户改了size)
|
|
_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(() {
|
|
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: [
|
|
// 视频内容
|
|
widget.child ?? _buildAnchorVideo(),
|
|
// 右上角关闭按钮
|
|
Positioned(
|
|
top: 2.w,
|
|
right: 2.w,
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
await _roomController.leaveChannel();
|
|
widget.onClose?.call();
|
|
},
|
|
child: Container(
|
|
width: 20.w,
|
|
height: 20.w,
|
|
decoration: BoxDecoration(
|
|
color: Colors.black.withOpacity(0.5),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(Icons.close, color: Colors.white, size: 14.w),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
}
|