282 lines
9.3 KiB
Dart
282 lines
9.3 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:flutter_svg/svg.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart';
|
||
import 'package:maibu_satabot_v2/features/remote_control/presentation/widgets/status_chip.dart';
|
||
import 'package:cc_ui_kit/cc_ui_kit.dart';
|
||
|
||
import '../../../devices/presentation/bloc/devices_cubit.dart';
|
||
import '../bloc/remote_control_cubit.dart';
|
||
|
||
class TopStatusBar extends StatelessWidget {
|
||
const TopStatusBar({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
// 监听全局设备状态
|
||
final deviceState = context.watch<DevicesCubit>().state;
|
||
final device = deviceState.selectedDevice;
|
||
ControlMode _parseControlMode(String modeString) {
|
||
switch (modeString.toUpperCase()) {
|
||
case 'BLE':
|
||
return ControlMode.BLE;
|
||
case 'LOCAL':
|
||
return ControlMode.LOCAL;
|
||
case 'TCP':
|
||
return ControlMode.TCP;
|
||
case 'NONE':
|
||
return ControlMode.NONE;
|
||
case 'OTHER':
|
||
return ControlMode.OTHER;
|
||
default:
|
||
return ControlMode.NONE;
|
||
}
|
||
}
|
||
|
||
// 监听局部遥控状态
|
||
final remoteState = context.watch<RemoteControlCubit>().state;
|
||
|
||
return Padding(
|
||
padding: const EdgeInsets.all(12.0),
|
||
child: Row(
|
||
children: [
|
||
// 1. 返回按钮 (对应 SimpleSmallFunctionButton)
|
||
_buildIconButton("assets/svgs/remote_back.svg", () => context.pop()),
|
||
const SizedBox(width: 16),
|
||
|
||
// 2. 控制状态 (对应 StatusChipLeft)
|
||
StatusChip(
|
||
text: remoteState.hasPermission ? "正在控制" : "未在控制",
|
||
color: remoteState.hasPermission
|
||
? const Color(0xFF1DB954)
|
||
: Colors.red,
|
||
icon: Icons.eighteen_mp,
|
||
breathing: !remoteState.hasPermission,
|
||
onTap: () {
|
||
if (!remoteState.hasPermission) {
|
||
// 弹出请求权限对话框逻辑
|
||
context.read<RemoteControlCubit>().togglePermissionDialog(true);
|
||
}
|
||
},
|
||
),
|
||
const SizedBox(width: 8),
|
||
|
||
// 3. 锁定状态 (对应 SmallFunctionButton)
|
||
_buildIconButton(
|
||
remoteState.isLocked
|
||
? "assets/svgs/remote_lock.svg"
|
||
: "assets/svgs/remote_lock_open.svg",
|
||
() => context.read<RemoteControlCubit>().toggleLock(),
|
||
isSelected: remoteState.isLocked,
|
||
),
|
||
const SizedBox(width: 8),
|
||
|
||
// 4. 刷新按钮
|
||
_buildIconButton("assets/svgs/remote_refresh.svg", () {}),
|
||
const SizedBox(width: 25),
|
||
// 5. 火技能按钮 (对应 SmallFunctionButton)
|
||
//_buildIconButton("assets/svgs/fire.svg", () {}),
|
||
_buildSliderBox("割刀", false,context),
|
||
|
||
const Spacer(),
|
||
|
||
// TODO
|
||
_buildControlModeChip( _parseControlMode(remoteState.runningStatusModel.controlMode)),
|
||
|
||
const SizedBox(width: 8),
|
||
|
||
_buildVoltageChip(remoteState.voltage),
|
||
|
||
const SizedBox(width: 8),
|
||
|
||
// 5. 信号延迟 (对应 pingStatusChip)
|
||
_buildPingChip(remoteState.ping),
|
||
const SizedBox(width: 8),
|
||
|
||
// 6. 电量 (对应 StatusChipRight)
|
||
_buildBatteryChip(remoteState.battery),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildIconButton(
|
||
String svgAsset, // 改为 String 路径,例如 'assets/icons/stop.svg'
|
||
VoidCallback onTap, {
|
||
bool isSelected = false,
|
||
}) {
|
||
return InkWell(
|
||
onTap: onTap,
|
||
borderRadius: BorderRadius.circular(10),
|
||
child: Container(
|
||
width: 32,
|
||
height: 32,
|
||
decoration: BoxDecoration(
|
||
color: isSelected
|
||
? const Color(0xCC0078D4)
|
||
: Colors.grey.withOpacity(0.4),
|
||
borderRadius: BorderRadius.circular(10),
|
||
border: Border.all(color: Colors.white.withOpacity(0.3), width: 0.5),
|
||
),
|
||
padding: const EdgeInsets.all(7),
|
||
child: SvgPicture.asset(
|
||
svgAsset,
|
||
// 这里可以使用 colorFilter 来改变 SVG 的颜色(如果需要根据选中状态变色)
|
||
colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildPingChip(int ping) {
|
||
Color color = ping < 100
|
||
? Colors.green
|
||
: (ping < 200 ? Colors.orange : Colors.red);
|
||
return StatusChip(
|
||
text: "$ping ms",
|
||
color: color.withValues(alpha: 0.6),
|
||
svgColor: color,
|
||
icon: Icons.network_check,
|
||
);
|
||
}
|
||
|
||
Widget _buildControlModeChip(ControlMode mode) {
|
||
String displayText;
|
||
switch (mode) {
|
||
case ControlMode.BLE:
|
||
displayText = '蓝牙模式';
|
||
break;
|
||
case ControlMode.LOCAL:
|
||
displayText = '本地模式';
|
||
break;
|
||
case ControlMode.TCP:
|
||
displayText = 'TCP模式';
|
||
break;
|
||
case ControlMode.NONE:
|
||
displayText = '无模式';
|
||
break;
|
||
case ControlMode.OTHER:
|
||
displayText = '其他模式';
|
||
break;
|
||
default:
|
||
displayText = '未知模式';
|
||
}
|
||
|
||
return StatusChip(
|
||
text: displayText,
|
||
color: Colors.blue.withValues(alpha: 0.6),
|
||
svgColor: Colors.blue,
|
||
svgPath: "assets/svgs/remote_net.svg",
|
||
);
|
||
}
|
||
|
||
Widget _buildVoltageChip(int voltage) {
|
||
return StatusChip(
|
||
text: "$voltage V",
|
||
color: Color(0xFFFACC15).withValues(alpha: 0.6),
|
||
svgColor: Color(0xFFFACC15),
|
||
svgPath: "assets/svgs/remote_zap.svg",
|
||
);
|
||
}
|
||
|
||
Widget _buildBatteryChip(int level) {
|
||
// 1. 根据数值判断图片路径 (逻辑与之前一致)
|
||
String svgPath;
|
||
Color color;
|
||
|
||
if (level >= 90) {
|
||
svgPath = "assets/svgs/remote_battery_full.svg";
|
||
color = Colors.green;
|
||
} else if (level >= 50) {
|
||
svgPath = "assets/svgs/remote_battery_middle.svg";
|
||
color = Colors.greenAccent;
|
||
} else if (level >= 20) {
|
||
svgPath = "assets/svgs/remote_battery_low.svg";
|
||
color = Colors.orange;
|
||
} else if (level >= 10) {
|
||
svgPath = "assets/svgs/remote_battery_warning.svg";
|
||
color = Colors.redAccent;
|
||
} else {
|
||
svgPath = "assets/svgs/remote_battery_none.svg";
|
||
color = Colors.red;
|
||
}
|
||
|
||
// 2. 使用 StatusChip 包装,但 icon 传入 SvgPicture
|
||
return StatusChip(
|
||
text: "$level%",
|
||
color: color.withValues(alpha: 0.6),
|
||
svgColor: color,
|
||
// 如果你的 StatusChip 已经改为接受 Widget,则直接传 SvgPicture
|
||
// 如果 StatusChip 目前只接受 IconData,你需要按我之前的建议给它加个 leading 参数
|
||
svgPath: svgPath,
|
||
);
|
||
}
|
||
|
||
Widget _buildSliderBox(String label, bool isLeft, BuildContext context) {
|
||
// 1. 核心尺寸调整:足够宽的容器解决拥挤,高度匹配状态栏
|
||
const double boxWidth = 96; // 水平宽度放大,容纳左右图标
|
||
const double boxHeight = 32; // 高度和其他按钮保持一致
|
||
const double iconSize = 20; // 图标尺寸放大,避免过小拥挤
|
||
|
||
return SizedBox(
|
||
width: boxWidth,
|
||
height: boxHeight,
|
||
child: Center(
|
||
// 2. 关键:旋转组件将竖向CCExpandSlider转为水平(无direction参数的替代方案)
|
||
child: RotatedBox(
|
||
quarterTurns: 1, // 旋转90度(竖向→横向),若方向反了可改为3
|
||
child: SizedBox(
|
||
// 旋转后宽高互换,这里给CCExpandSlider足够的显示空间
|
||
width: boxHeight, // 旋转后对应原高度
|
||
height: boxWidth, // 旋转后对应原宽度(足够长,不拥挤)
|
||
child: CCExpandSlider(
|
||
label: " ",
|
||
// 3. 图标适配旋转后的方向(仍用上下箭头,旋转后变为左右)因为外边旋转了 90度 参数传递也要转换
|
||
svgStart: "assets/svgs/remote_flame_minus.svg", // 旋转后→左箭头
|
||
svgCenter: "assets/svgs/remote_flame.svg",
|
||
svgEnd: "assets/svgs/remote_flame_plus.svg", // 旋转后→右箭头
|
||
// 4. 放大图标尺寸,解决拥挤
|
||
iconSize: iconSize,
|
||
// 保留原有业务逻辑
|
||
onStart: () => _handleSliderAction(label, "start", context),
|
||
onCenter: () => _handleSliderAction(label, "center", context),
|
||
onEnd: () => _handleSliderAction(label, "end", context),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
// 🔥 新增:统一的业务逻辑分发方法
|
||
void _handleSliderAction(String label, String action, BuildContext context) {
|
||
debugPrint('🎯 [Slider 业务] label: $label, action: $action');
|
||
|
||
// 🔥 根据 label 区分不同的设备
|
||
_handleChassisAction(action, context);
|
||
}
|
||
|
||
// 🔥 点火的业务逻辑(左滑块)
|
||
void _handleChassisAction(String action, BuildContext context) {
|
||
final cubit = context.read<RemoteControlCubit>();
|
||
|
||
switch (action) {
|
||
case "start":
|
||
debugPrint('2熄火');
|
||
// TODO: 发送 🔥点火指令
|
||
cubit.sendFireCommand(2);
|
||
break;
|
||
case "center":
|
||
debugPrint('️0熄火');
|
||
// TODO: 发送点火停止指令
|
||
cubit.sendFireCommand(0);
|
||
break;
|
||
case "end":
|
||
debugPrint('1点火)');
|
||
// TODO: 发送熄火后退指令
|
||
cubit.sendFireCommand(1);
|
||
break;
|
||
}
|
||
}
|
||
}
|