Files
flutterApp/lib/features/remote_control/presentation/widgets/top_status_bar.dart

282 lines
9.3 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}