diff --git a/lib/features/home/presentation/widgets/map/testmap_pages.dart b/lib/features/home/presentation/widgets/map/testmap_pages.dart index 11feb47d..63f88c21 100644 --- a/lib/features/home/presentation/widgets/map/testmap_pages.dart +++ b/lib/features/home/presentation/widgets/map/testmap_pages.dart @@ -1729,8 +1729,9 @@ class _MapPageEnterpriseState extends State { isStartWork = false; }); _saveDataToLocal(); - if (isStopWork || !isStartWork) return; _stopWork(); + + //if (isStopWork || !isStartWork) return; }, icon: const Icon(Icons.close, color: Colors.grey, size: 20), padding: EdgeInsets.zero, diff --git a/lib/features/remote_control/presentation/pages/remote_control_page.dart b/lib/features/remote_control/presentation/pages/remote_control_page.dart index 3b0c0601..52861c86 100644 --- a/lib/features/remote_control/presentation/pages/remote_control_page.dart +++ b/lib/features/remote_control/presentation/pages/remote_control_page.dart @@ -82,6 +82,7 @@ class _RemoteControlPageState extends State { streamUrl: _videoStreamUrl, showLeftPip: state.showLeftPip, // 从 Cubit 状态中读取 showRightPip: state.showRightPip, // 从 Cubit 状态中读取 + isFrontMain: context.watch().state.controlEntity.originY>= 0.5, ); }, ), @@ -192,21 +193,21 @@ class _RemoteControlPageState extends State { color: Colors.black54, child: AlertDialog( title: const Text("权限变更"), - // content: Text("${state.permissionPlatform}端正请求控制权,同意释放吗?"), + // content: Text("${state.permissionPlatform}端正请求控制权,同意释放吗?"), content: Text("web端正请求控制权,同意释放吗?"), actions: [ - TextButton(onPressed: () => context.read().respondPermission(false,deviceId), child: const Text("拒绝")), + TextButton(onPressed: () => context.read().respondPermission(false, deviceId), child: const Text("拒绝")), TextButton( - onPressed: () { - context.read().respondPermission(true, deviceId); - // 🔥 用户同意,延迟跳转到首页 - // Future.delayed(const Duration(milliseconds: 1000), () { - // if (context.mounted) { - // context.go(RoutePaths.home); - // } - // }); - }, - child: const Text("同意"), + onPressed: () { + context.read().respondPermission(true, deviceId); + // 🔥 用户同意,延迟跳转到首页 + // Future.delayed(const Duration(milliseconds: 1000), () { + // if (context.mounted) { + // context.go(RoutePaths.home); + // } + // }); + }, + child: const Text("同意"), ), ], ), diff --git a/lib/features/remote_control/presentation/widgets/webrtc/webrtc_local_player.dart b/lib/features/remote_control/presentation/widgets/webrtc/webrtc_local_player.dart index 931b7ed7..79434b10 100644 --- a/lib/features/remote_control/presentation/widgets/webrtc/webrtc_local_player.dart +++ b/lib/features/remote_control/presentation/widgets/webrtc/webrtc_local_player.dart @@ -9,11 +9,15 @@ class WebRTCLocalPlayer extends StatefulWidget { final bool showLeftPip; final bool showRightPip; + // 🔥 新增:外部传入是否显示前视角 + final bool isFrontMain; + const WebRTCLocalPlayer({ super.key, required this.streamUrl, this.showLeftPip = true, this.showRightPip = true, + required this.isFrontMain, // 🔥 必传 }); @override @@ -25,15 +29,10 @@ class _WebRTCLocalPlayerState extends State { RTCPeerConnection? _peerConnection; bool _isInitialized = false; - bool _isFrontMain = true; // 使用 ValueNotifier 配合局部刷新,提升拖拽性能 - final ValueNotifier _leftPosNotifier = ValueNotifier( - const Offset(20, 80), - ); - final ValueNotifier _rightPosNotifier = ValueNotifier( - const Offset(200, 80), - ); + final ValueNotifier _leftPosNotifier = ValueNotifier(const Offset(20, 80)); + final ValueNotifier _rightPosNotifier = ValueNotifier(const Offset(200, 80)); bool _isPosInitialized = false; @override @@ -73,32 +72,21 @@ class _WebRTCLocalPlayerState extends State { }; try { - RTCSessionDescription offer = await _peerConnection!.createOffer({ - 'offerToReceiveVideo': 1, - }); + RTCSessionDescription offer = await _peerConnection!.createOffer({'offerToReceiveVideo': 1}); await _peerConnection!.setLocalDescription(offer); - Uri uri = Uri.parse( - widget.streamUrl.replaceFirst('webrtc://', 'http://'), - ); + Uri uri = Uri.parse(widget.streamUrl.replaceFirst('webrtc://', 'http://')); String apiUrl = "http://${uri.host}:1985/rtc/v1/play/"; final response = await http.post( Uri.parse(apiUrl), headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - "api": apiUrl, - "streamurl": widget.streamUrl, - "clientip": null, - "sdp": offer.sdp, - }), + body: jsonEncode({"api": apiUrl, "streamurl": widget.streamUrl, "clientip": null, "sdp": offer.sdp}), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); - await _peerConnection!.setRemoteDescription( - RTCSessionDescription(data['sdp'], 'answer'), - ); + await _peerConnection!.setRemoteDescription(RTCSessionDescription(data['sdp'], 'answer')); } } catch (e) { debugPrint("信令错误: $e"); @@ -117,8 +105,7 @@ class _WebRTCLocalPlayerState extends State { // 四分屏裁剪视图 Widget _buildQuadrantView({required Alignment alignment}) { - if (!_isInitialized || _renderer.srcObject == null) - return Container(color: Colors.black); + if (!_isInitialized || _renderer.srcObject == null) return Container(color: Colors.black); return RepaintBoundary( child: ClipRect( @@ -126,11 +113,7 @@ class _WebRTCLocalPlayerState extends State { widthFactor: 2.0, heightFactor: 2.0, alignment: alignment, - child: RTCVideoView( - _renderer, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, - mirror: false, - ), + child: RTCVideoView(_renderer, objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, mirror: false), ), ), ); @@ -143,12 +126,7 @@ class _WebRTCLocalPlayerState extends State { return const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.white, - Colors.white, - Colors.transparent, - ], + colors: [Colors.transparent, Colors.white, Colors.white, Colors.transparent], stops: [0.0, 0.15, 0.85, 1.0], ).createShader(bounds); }, @@ -158,19 +136,12 @@ class _WebRTCLocalPlayerState extends State { return const LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, - colors: [ - Colors.transparent, - Colors.white, - Colors.white, - Colors.transparent, - ], + colors: [Colors.transparent, Colors.white, Colors.white, Colors.transparent], stops: [0.0, 0.05, 0.95, 1.0], ).createShader(bounds); }, blendMode: BlendMode.dstIn, - child: _buildQuadrantView( - alignment: _isFrontMain ? Alignment.topLeft : Alignment.topRight, - ), + child: _buildQuadrantView(alignment: widget.isFrontMain ? Alignment.topLeft : Alignment.topRight), ), ); } @@ -199,10 +170,7 @@ class _WebRTCLocalPlayerState extends State { if (!_isPosInitialized) { _leftPosNotifier.value = const Offset(20, 80); - _rightPosNotifier.value = Offset( - constraints.maxWidth - pipW - 20, - 80, - ); + _rightPosNotifier.value = Offset(constraints.maxWidth - pipW - 20, 80); _isPosInitialized = true; } @@ -212,11 +180,7 @@ class _WebRTCLocalPlayerState extends State { Positioned.fill( child: Stack( children: [ - _buildQuadrantView( - alignment: _isFrontMain - ? Alignment.topLeft - : Alignment.topRight, - ), + _buildQuadrantView(alignment: widget.isFrontMain ? Alignment.topLeft : Alignment.topRight), Positioned.fill( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20), @@ -227,35 +191,14 @@ class _WebRTCLocalPlayerState extends State { ), ), - // ② 主视频层 + // ② 主视频层(🔥 去掉双击,交给外部控制) Center( - child: GestureDetector( - onDoubleTap: () => - setState(() => _isFrontMain = !_isFrontMain), - child: AspectRatio( - aspectRatio: 16 / 9, - child: _buildMainViewWithFeathering(), - ), - ), + child: AspectRatio(aspectRatio: 16 / 9, child: _buildMainViewWithFeathering()), ), // ③ 悬浮小窗 - 解决阻尼感的 1:1 稳定拖拽 - if (widget.showLeftPip) - _buildFastPip( - _leftPosNotifier, - Alignment.bottomLeft, - constraints, - pipW, - pipH, - ), - if (widget.showRightPip) - _buildFastPip( - _rightPosNotifier, - Alignment.bottomRight, - constraints, - pipW, - pipH, - ), + if (widget.showLeftPip) _buildFastPip(_leftPosNotifier, Alignment.bottomLeft, constraints, pipW, pipH), + if (widget.showRightPip) _buildFastPip(_rightPosNotifier, Alignment.bottomRight, constraints, pipW, pipH), ], ); }, @@ -263,12 +206,7 @@ class _WebRTCLocalPlayerState extends State { ); } - void _correctPosition( - ValueNotifier notifier, - BoxConstraints constraints, - double w, - double h, - ) { + void _correctPosition(ValueNotifier notifier, BoxConstraints constraints, double w, double h) { double maxX = (constraints.maxWidth - w).clamp(0.0, double.infinity); double maxY = (constraints.maxHeight - h).clamp(0.0, double.infinity); double newX = notifier.value.dx.clamp(0.0, maxX); @@ -279,13 +217,7 @@ class _WebRTCLocalPlayerState extends State { } // 极致丝滑的局部刷新组件 - Widget _buildFastPip( - ValueNotifier notifier, - Alignment align, - BoxConstraints constraints, - double w, - double h, - ) { + Widget _buildFastPip(ValueNotifier notifier, Alignment align, BoxConstraints constraints, double w, double h) { return ValueListenableBuilder( valueListenable: notifier, builder: (context, pos, child) { @@ -296,14 +228,8 @@ class _WebRTCLocalPlayerState extends State { behavior: HitTestBehavior.opaque, onPanUpdate: (details) { // 关键修正:使用 notifier.value.dx 替换 pos.dx,消除位移丢失导致的阻尼感 - final double nextX = (notifier.value.dx + details.delta.dx).clamp( - 0.0, - (constraints.maxWidth - w).clamp(0.0, double.infinity), - ); - final double nextY = (notifier.value.dy + details.delta.dy).clamp( - 0.0, - (constraints.maxHeight - h).clamp(0.0, double.infinity), - ); + final double nextX = (notifier.value.dx + details.delta.dx).clamp(0.0, (constraints.maxWidth - w).clamp(0.0, double.infinity)); + final double nextY = (notifier.value.dy + details.delta.dy).clamp(0.0, (constraints.maxHeight - h).clamp(0.0, double.infinity)); notifier.value = Offset(nextX, nextY); }, child: child!, @@ -315,13 +241,7 @@ class _WebRTCLocalPlayerState extends State { height: h, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.5), - blurRadius: 20, - offset: const Offset(0, 8), - ), - ], + boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 20, offset: const Offset(0, 8))], ), clipBehavior: Clip.antiAlias, child: _buildQuadrantView(alignment: align),