Files
2026-01-29 19:16:41 +08:00

332 lines
11 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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/widgets/status_chip.dart';
import '../../../../core/network/enums/control_mode.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;
// 监听局部遥控状态
final remoteState = context.watch<RemoteControlCubit>().state;
var _remoteControlCubit = context.read<RemoteControlCubit>();
return Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: [
// 1. 返回按钮 (对应 SimpleSmallFunctionButton)
_buildIconButton("assets/svgs/remote_back.svg", () async {
_remoteControlCubit.stopControlLoop();
_remoteControlCubit.disposeVideo();
if (context.mounted) 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) {
// 弹出请求权限对话框逻辑
_remoteControlCubit.togglePermissionDialog(true);
}
},
),
const SizedBox(width: 8),
// 3. 锁定状态 (对应 SmallFunctionButton)
_buildIconButton(
remoteState.isLocked
? "assets/svgs/remote_lock.svg"
: "assets/svgs/remote_lock_open.svg",
() => _remoteControlCubit.toggleLock(),
isSelected: remoteState.isLocked,
),
const SizedBox(width: 8),
// 4. 刷新按钮
_buildIconButton("assets/svgs/remote_refresh.svg", () {}),
const Spacer(),
_buildExpandIconButton(
iconPath: "assets/svgs/remote_expand.svg",
selectedIconPath: "assets/svgs/remote_unexpand.svg",
isSelected: remoteState.topRightIsExpanded,
onTap: (){
_remoteControlCubit.toggleTopLeftExpand();
},
),
const SizedBox(width: 5),
AnimatedSize(
duration: const Duration(milliseconds: 100),
curve: Curves.easeInOut,
child: Row(
children: !remoteState.topRightIsExpanded
? [
const SizedBox(width: 8),
_buildSwitchIconButton(
iconPath: "assets/svgs/remote_recognition_off.svg",
selectedIconPath: "assets/svgs/remote_recognition.svg",
isSelected: remoteState.obstacleRecognitionFlag,
onTap: (){
_remoteControlCubit.toggleObstacleRecognition();
},
),
const SizedBox(width: 8),
_buildSwitchIconButton(
iconPath: "assets/svgs/remote_video_left_off.svg",
selectedIconPath: "assets/svgs/remote_video_left.svg",
isSelected: remoteState.showLeftPip,
onTap: (){
_remoteControlCubit.toggleLeftPip();
},
),
const SizedBox(width: 8),
_buildSwitchIconButton(
iconPath: "assets/svgs/remote_video_right_off.svg",
selectedIconPath: "assets/svgs/remote_video_right.svg",
isSelected: remoteState.showRightPip,
onTap: (){
_remoteControlCubit.toggleRightPip();
},
),
const SizedBox(width: 8),
_buildControlModeChip(remoteState.runningStatusEntity.controlMode),
const SizedBox(width: 8),
_buildVoltageChip(remoteState.runningStatusEntity.voltage),
const SizedBox(width: 8),
_buildPingChip(remoteState.ping),
]
: [], // 折叠时数组为空
),
),
// 5. 信号延迟 (对应 pingStatusChip)
// _buildPingChip(remoteState.ping),
const SizedBox(width: 8),
// 6. 电量 (对应 StatusChipRight)
_buildBatteryChip(remoteState.runningStatusEntity.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 _buildSwitchIconButton({
required String iconPath, // 默认图标路径
required String selectedIconPath, // 选中后的图标路径
required VoidCallback onTap, // 点击事件
bool isSelected = false, // 是否选中状态
double size = 32, // 按钮尺寸
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
// 增加颜色切换动画
width: size,
height: size,
decoration: BoxDecoration(
// 选中时为深蓝色,未选中时为半透明灰色
color: isSelected
? const Color(0xFF0078D4)
: Colors.grey.withOpacity(0.4),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isSelected
? Colors.transparent
: Colors.white.withOpacity(0.3),
width: 0.5,
),
boxShadow: isSelected
? [
BoxShadow(
color: const Color(0xFF0078D4).withOpacity(0.4),
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: null,
),
padding: const EdgeInsets.all(7),
child: SvgPicture.asset(
// 根据状态切换图片
isSelected ? selectedIconPath : iconPath,
colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
),
),
);
}
Widget _buildExpandIconButton({
required String iconPath, // 默认图标路径
required String selectedIconPath, // 选中后的图标路径
required VoidCallback onTap, // 点击事件
bool isSelected = false, // 是否选中状态
double size = 28, // 按钮尺寸
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
// 增加颜色切换动画
width: size,
height: size,
decoration: BoxDecoration(
// 选中时为深蓝色,未选中时为半透明灰色
color: Colors.grey.withOpacity(0.4),
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 0.5,
),
boxShadow: null,
),
padding: const EdgeInsets.all(7),
child: SvgPicture.asset(
// 根据状态切换图片
isSelected ? selectedIconPath : iconPath,
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;
}
return StatusChip(
text: displayText,
color: Colors.blue.withValues(alpha: 0.6),
svgColor: Colors.blue,
svgPath: "assets/svgs/remote_net.svg",
);
}
Widget _buildVoltageChip(double voltage) {
return StatusChip(
text: "$voltage V",
color: Color(0xFFFACC15).withValues(alpha: 0.6),
svgColor: Color(0xFFFACC15),
svgPath: "assets/svgs/remote_zap.svg",
);
}
Widget _buildBatteryChip(double 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,
);
}
}