远程遥控重构
This commit is contained in:
@@ -1,69 +1,88 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'package:cc_ui_kit/cc_ui_kit.dart';
|
import 'package:cc_ui_kit/cc_ui_kit.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:vibration/vibration.dart';
|
import 'package:vibration/vibration.dart';
|
||||||
|
|
||||||
import '../bloc/remote_control_cubit.dart';
|
import '../bloc/remote_control_cubit.dart';
|
||||||
|
|
||||||
class LeftJoystickArea extends StatelessWidget {
|
class LeftJoystickArea extends StatefulWidget {
|
||||||
final bool isLocked;
|
final bool isLocked;
|
||||||
final double width;
|
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<LeftJoystickArea> createState() => _LeftJoystickAreaState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LeftJoystickAreaState extends State<LeftJoystickArea> {
|
||||||
|
int _lastSentY = 0;
|
||||||
|
bool _isTouching = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min, // 紧凑布局
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// 使用 IgnorePointer 处理锁定逻辑,对应 Android 的 isLocked 判断
|
|
||||||
IgnorePointer(
|
IgnorePointer(
|
||||||
ignoring: isLocked,
|
ignoring: widget.isLocked,
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
opacity: isLocked ? 0.3 : 1.0, // 锁定后变透明
|
opacity: widget.isLocked ? 0.3 : 1.0,
|
||||||
child: CCJoystick(
|
child: CCJoystick(
|
||||||
radius: width, // 对应 size(200.dp)
|
radius: widget.width,
|
||||||
axisHint: AxisHint.forwardBackward,
|
axisHint: AxisHint.forwardBackward,
|
||||||
onValueChanged: (value) {
|
onValueChanged: (value) {
|
||||||
debugPrint('🎮 [左摇杆] value: ${value.y},${value.x}');
|
// 标记:正在触摸
|
||||||
// 对应 viewModel.updateOriginY(y)
|
_isTouching = true;
|
||||||
context.read<RemoteControlCubit>().updateOriginY(value.y);
|
|
||||||
Future.delayed(const Duration(milliseconds: 50), () {
|
int currentY = value.y.toInt();
|
||||||
_triggerVibration();
|
if (currentY != _lastSentY) {
|
||||||
});
|
_lastSentY = currentY;
|
||||||
|
debugPrint('左摇杆的数据- x: 0, y: $currentY');
|
||||||
|
context.read<RemoteControlCubit>().updateOriginY(currentY);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onPress: () {
|
onPress: () {
|
||||||
// 对应 VibrateOnce(current, 100)
|
_isTouching = true;
|
||||||
//HapticFeedback.mediumImpact();
|
|
||||||
_triggerVibration();
|
_triggerVibration();
|
||||||
},
|
},
|
||||||
|
onPanEnd: () {
|
||||||
|
// 🔥 松手 100% 归零
|
||||||
|
_stopJoystick();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 18),
|
const SizedBox(height: 18),
|
||||||
Text(
|
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()
|
void _stopJoystick() {
|
||||||
.then((hasVibrator) {
|
_isTouching = false;
|
||||||
if (hasVibrator ?? false) {
|
_lastSentY = 0;
|
||||||
Vibration.vibrate(duration: 12);
|
debugPrint('✅ 摇杆已停止 -> 强制 Y=0 停车');
|
||||||
debugPrint('📳 [右摇杆] 震动执行 - 10ms');
|
context.read<RemoteControlCubit>().updateOriginY(0);
|
||||||
} else {
|
|
||||||
debugPrint('⚠️ [右摇杆] 设备不支持震动');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catchError((e) {
|
|
||||||
debugPrint('❌ [右摇杆] 震动失败:$e');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
void _triggerVibration() {
|
||||||
|
Vibration.hasVibrator().then((has) {
|
||||||
|
if (has ?? false) Vibration.vibrate(duration: 12);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,74 +1,75 @@
|
|||||||
import 'package:cc_ui_kit/cc_ui_kit.dart';
|
import 'package:cc_ui_kit/cc_ui_kit.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:vibration/vibration.dart';
|
import 'package:vibration/vibration.dart';
|
||||||
|
|
||||||
import '../bloc/remote_control_cubit.dart';
|
import '../bloc/remote_control_cubit.dart';
|
||||||
|
|
||||||
class RightJoystickArea extends StatelessWidget {
|
class RightJoystickArea extends StatefulWidget {
|
||||||
final bool isLocked;
|
final bool isLocked;
|
||||||
final double width;
|
final double width;
|
||||||
|
|
||||||
|
// 修复:删除重复的 key 定义
|
||||||
const RightJoystickArea({
|
const RightJoystickArea({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.isLocked,
|
required this.isLocked,
|
||||||
required this.width,
|
required this.width,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RightJoystickArea> createState() => _RightJoystickAreaState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RightJoystickAreaState extends State<RightJoystickArea> {
|
||||||
|
int _lastX = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min, // 垂直方向紧凑布局
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// 1. 交互锁定逻辑:对应 Compose 的 isLocked 判断
|
|
||||||
IgnorePointer(
|
IgnorePointer(
|
||||||
ignoring: isLocked,
|
ignoring: widget.isLocked,
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
opacity: isLocked ? 0.3 : 1.0, // 锁定后变透明/灰色
|
opacity: widget.isLocked ? 0.3 : 1.0,
|
||||||
child: CCJoystick(
|
child: CCJoystick(
|
||||||
radius: width, // 对应 size(200.dp)
|
radius: widget.width,
|
||||||
axisHint: AxisHint.leftRight, // 关键:指定为左右控制
|
axisHint: AxisHint.leftRight,
|
||||||
onValueChanged: (value) {
|
onValueChanged: (value) {
|
||||||
// 对应 viewModel.updateOriginX(x)
|
int currentX = value.x.toInt();
|
||||||
/// context.read<RemoteControlCubit>().updateOriginY(value.x);
|
if (currentX == _lastX) return;
|
||||||
context.read<RemoteControlCubit>().updateOriginX(value.x);
|
_lastX = currentX;
|
||||||
Future.delayed(const Duration(milliseconds: 50), () {
|
|
||||||
_triggerVibration();
|
int finalX = currentX.abs() < 1 ? 0 : currentX;
|
||||||
});
|
debugPrint('右摇杆的数据- x: $finalX, y: 0');
|
||||||
|
context.read<RemoteControlCubit>().updateOriginX(finalX);
|
||||||
|
},
|
||||||
|
onPress: _triggerVibration,
|
||||||
|
onPanEnd: () {
|
||||||
|
debugPrint('🕹️ [右摇杆] 松手,强制X=0');
|
||||||
|
_lastX = 0;
|
||||||
|
context.read<RemoteControlCubit>().updateOriginX(0);
|
||||||
},
|
},
|
||||||
onPress: () {
|
|
||||||
_triggerVibration();
|
|
||||||
}
|
|
||||||
,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 18),
|
const SizedBox(height: 18),
|
||||||
// 2. 底部文字:对应 Text("左右控制", color = Color.Gray)
|
|
||||||
Text(
|
Text(
|
||||||
"左右控制",
|
"左右控制",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white.withValues(alpha: 0.5),
|
color: Colors.white.withOpacity(0.5),
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _triggerVibration() {
|
void _triggerVibration() {
|
||||||
Vibration.hasVibrator().then((hasVibrator) {
|
Vibration.hasVibrator().then((has) {
|
||||||
if (hasVibrator ?? false) {
|
if (has ?? false) Vibration.vibrate(duration: 12);
|
||||||
Vibration.vibrate(duration: 12);
|
|
||||||
debugPrint('📳 [右摇杆] 震动执行 - 100ms');
|
|
||||||
} else {
|
|
||||||
debugPrint('⚠️ [右摇杆] 设备不支持震动');
|
|
||||||
}
|
|
||||||
}).catchError((e) {
|
|
||||||
debugPrint('❌ [右摇杆] 震动失败:$e');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ class CCJoystick extends StatefulWidget {
|
|||||||
final AxisHint axisHint;
|
final AxisHint axisHint;
|
||||||
final Function(JoystickValue) onValueChanged;
|
final Function(JoystickValue) onValueChanged;
|
||||||
final VoidCallback? onPress;
|
final VoidCallback? onPress;
|
||||||
|
final VoidCallback? onPanEnd; // 🔥 新增:松手回调
|
||||||
final double radius;
|
final double radius;
|
||||||
|
|
||||||
const CCJoystick({
|
const CCJoystick({
|
||||||
@@ -21,6 +22,7 @@ class CCJoystick extends StatefulWidget {
|
|||||||
required this.axisHint,
|
required this.axisHint,
|
||||||
required this.onValueChanged,
|
required this.onValueChanged,
|
||||||
this.onPress,
|
this.onPress,
|
||||||
|
this.onPanEnd, // 🔥 新增
|
||||||
this.radius = 80,
|
this.radius = 80,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -70,6 +72,7 @@ class _CustomJoystickState extends State<CCJoystick>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque, // 🔥 改为 opaque,确保所有触摸事件都被捕获
|
||||||
onPanStart: (_) => widget.onPress?.call(),
|
onPanStart: (_) => widget.onPress?.call(),
|
||||||
onPanUpdate: (details) {
|
onPanUpdate: (details) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -91,6 +94,7 @@ class _CustomJoystickState extends State<CCJoystick>
|
|||||||
onPanEnd: (_) {
|
onPanEnd: (_) {
|
||||||
setState(() => _offset = Offset.zero); // 归位
|
setState(() => _offset = Offset.zero); // 归位
|
||||||
_updateValue();
|
_updateValue();
|
||||||
|
widget.onPanEnd?.call(); // 🔥 调用松手回调
|
||||||
},
|
},
|
||||||
child: AnimatedBuilder(
|
child: AnimatedBuilder(
|
||||||
animation: _floatController,
|
animation: _floatController,
|
||||||
|
|||||||
Reference in New Issue
Block a user