From c45f8ee86783119f5ffacda69282eba5df11c8d1 Mon Sep 17 00:00:00 2001 From: Songzex <2402265378@qq.com> Date: Thu, 16 Apr 2026 10:48:50 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E9=81=A5=E6=8E=A7=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/left_joystick_area.dart | 83 ++++++++++++------- .../widgets/right_joystick_area.dart | 67 +++++++-------- packages/cc_ui_kit/lib/src/cc_joystick.dart | 4 + 3 files changed, 89 insertions(+), 65 deletions(-) diff --git a/lib/features/remote_control/presentation/widgets/left_joystick_area.dart b/lib/features/remote_control/presentation/widgets/left_joystick_area.dart index 0870ddf4..a0d43d9d 100644 --- a/lib/features/remote_control/presentation/widgets/left_joystick_area.dart +++ b/lib/features/remote_control/presentation/widgets/left_joystick_area.dart @@ -1,69 +1,88 @@ +import 'dart:async'; import 'package:cc_ui_kit/cc_ui_kit.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:vibration/vibration.dart'; import '../bloc/remote_control_cubit.dart'; -class LeftJoystickArea extends StatelessWidget { +class LeftJoystickArea extends StatefulWidget { final bool isLocked; final double width; - const LeftJoystickArea({Key? key, required this.isLocked, required this.width}) : super(key: key); + const LeftJoystickArea({ + Key? key, + required this.isLocked, + required this.width, + }) : super(key: key); + + @override + State createState() => _LeftJoystickAreaState(); +} + +class _LeftJoystickAreaState extends State { + int _lastSentY = 0; + bool _isTouching = false; @override Widget build(BuildContext context) { return Column( - mainAxisSize: MainAxisSize.min, // 紧凑布局 + mainAxisSize: MainAxisSize.min, children: [ - // 使用 IgnorePointer 处理锁定逻辑,对应 Android 的 isLocked 判断 IgnorePointer( - ignoring: isLocked, + ignoring: widget.isLocked, child: AnimatedOpacity( duration: const Duration(milliseconds: 300), - opacity: isLocked ? 0.3 : 1.0, // 锁定后变透明 + opacity: widget.isLocked ? 0.3 : 1.0, child: CCJoystick( - radius: width, // 对应 size(200.dp) + radius: widget.width, axisHint: AxisHint.forwardBackward, onValueChanged: (value) { - debugPrint('🎮 [左摇杆] value: ${value.y},${value.x}'); - // 对应 viewModel.updateOriginY(y) - context.read().updateOriginY(value.y); - Future.delayed(const Duration(milliseconds: 50), () { - _triggerVibration(); - }); + // 标记:正在触摸 + _isTouching = true; + + int currentY = value.y.toInt(); + if (currentY != _lastSentY) { + _lastSentY = currentY; + debugPrint('左摇杆的数据- x: 0, y: $currentY'); + context.read().updateOriginY(currentY); + } }, onPress: () { - // 对应 VibrateOnce(current, 100) - //HapticFeedback.mediumImpact(); + _isTouching = true; _triggerVibration(); }, + onPanEnd: () { + // 🔥 松手 100% 归零 + _stopJoystick(); + }, ), ), ), const SizedBox(height: 18), Text( "前后控制", - style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 12, fontWeight: FontWeight.w500), + style: TextStyle( + color: Colors.white.withOpacity(0.5), + fontSize: 12, + fontWeight: FontWeight.w500, + ), ), - const SizedBox(height: 2), ], ); } - void _triggerVibration() { - Vibration.hasVibrator() - .then((hasVibrator) { - if (hasVibrator ?? false) { - Vibration.vibrate(duration: 12); - debugPrint('📳 [右摇杆] 震动执行 - 10ms'); - } else { - debugPrint('⚠️ [右摇杆] 设备不支持震动'); - } - }) - .catchError((e) { - debugPrint('❌ [右摇杆] 震动失败:$e'); - }); + // 统一停车方法(万能保险) + void _stopJoystick() { + _isTouching = false; + _lastSentY = 0; + debugPrint('✅ 摇杆已停止 -> 强制 Y=0 停车'); + context.read().updateOriginY(0); } -} + + void _triggerVibration() { + Vibration.hasVibrator().then((has) { + if (has ?? false) Vibration.vibrate(duration: 12); + }); + } +} \ No newline at end of file diff --git a/lib/features/remote_control/presentation/widgets/right_joystick_area.dart b/lib/features/remote_control/presentation/widgets/right_joystick_area.dart index 7710268a..5d6db415 100644 --- a/lib/features/remote_control/presentation/widgets/right_joystick_area.dart +++ b/lib/features/remote_control/presentation/widgets/right_joystick_area.dart @@ -1,74 +1,75 @@ import 'package:cc_ui_kit/cc_ui_kit.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:vibration/vibration.dart'; import '../bloc/remote_control_cubit.dart'; -class RightJoystickArea extends StatelessWidget { +class RightJoystickArea extends StatefulWidget { final bool isLocked; final double width; + // 修复:删除重复的 key 定义 const RightJoystickArea({ - Key? key, + super.key, required this.isLocked, required this.width, - }) : super(key: key); + }); + + @override + State createState() => _RightJoystickAreaState(); +} + +class _RightJoystickAreaState extends State { + int _lastX = 0; @override Widget build(BuildContext context) { return Column( - mainAxisSize: MainAxisSize.min, // 垂直方向紧凑布局 + mainAxisSize: MainAxisSize.min, children: [ - // 1. 交互锁定逻辑:对应 Compose 的 isLocked 判断 IgnorePointer( - ignoring: isLocked, + ignoring: widget.isLocked, child: AnimatedOpacity( duration: const Duration(milliseconds: 300), - opacity: isLocked ? 0.3 : 1.0, // 锁定后变透明/灰色 + opacity: widget.isLocked ? 0.3 : 1.0, child: CCJoystick( - radius: width, // 对应 size(200.dp) - axisHint: AxisHint.leftRight, // 关键:指定为左右控制 + radius: widget.width, + axisHint: AxisHint.leftRight, onValueChanged: (value) { - // 对应 viewModel.updateOriginX(x) - /// context.read().updateOriginY(value.x); - context.read().updateOriginX(value.x); - Future.delayed(const Duration(milliseconds: 50), () { - _triggerVibration(); - }); + int currentX = value.x.toInt(); + if (currentX == _lastX) return; + _lastX = currentX; + + int finalX = currentX.abs() < 1 ? 0 : currentX; + debugPrint('右摇杆的数据- x: $finalX, y: 0'); + context.read().updateOriginX(finalX); + }, + onPress: _triggerVibration, + onPanEnd: () { + debugPrint('🕹️ [右摇杆] 松手,强制X=0'); + _lastX = 0; + context.read().updateOriginX(0); }, - onPress: () { - _triggerVibration(); - } - , ), ), ), const SizedBox(height: 18), - // 2. 底部文字:对应 Text("左右控制", color = Color.Gray) Text( "左右控制", style: TextStyle( - color: Colors.white.withValues(alpha: 0.5), + color: Colors.white.withOpacity(0.5), fontSize: 12, fontWeight: FontWeight.w500, ), ), - const SizedBox(height: 2), ], ); } + void _triggerVibration() { - Vibration.hasVibrator().then((hasVibrator) { - if (hasVibrator ?? false) { - Vibration.vibrate(duration: 12); - debugPrint('📳 [右摇杆] 震动执行 - 100ms'); - } else { - debugPrint('⚠️ [右摇杆] 设备不支持震动'); - } - }).catchError((e) { - debugPrint('❌ [右摇杆] 震动失败:$e'); + Vibration.hasVibrator().then((has) { + if (has ?? false) Vibration.vibrate(duration: 12); }); } -} +} \ No newline at end of file diff --git a/packages/cc_ui_kit/lib/src/cc_joystick.dart b/packages/cc_ui_kit/lib/src/cc_joystick.dart index 28998b59..fa5ebf3a 100644 --- a/packages/cc_ui_kit/lib/src/cc_joystick.dart +++ b/packages/cc_ui_kit/lib/src/cc_joystick.dart @@ -14,6 +14,7 @@ class CCJoystick extends StatefulWidget { final AxisHint axisHint; final Function(JoystickValue) onValueChanged; final VoidCallback? onPress; + final VoidCallback? onPanEnd; // 🔥 新增:松手回调 final double radius; const CCJoystick({ @@ -21,6 +22,7 @@ class CCJoystick extends StatefulWidget { required this.axisHint, required this.onValueChanged, this.onPress, + this.onPanEnd, // 🔥 新增 this.radius = 80, }); @@ -70,6 +72,7 @@ class _CustomJoystickState extends State @override Widget build(BuildContext context) { return GestureDetector( + behavior: HitTestBehavior.opaque, // 🔥 改为 opaque,确保所有触摸事件都被捕获 onPanStart: (_) => widget.onPress?.call(), onPanUpdate: (details) { setState(() { @@ -91,6 +94,7 @@ class _CustomJoystickState extends State onPanEnd: (_) { setState(() => _offset = Offset.zero); // 归位 _updateValue(); + widget.onPanEnd?.call(); // 🔥 调用松手回调 }, child: AnimatedBuilder( animation: _floatController,