Files
feature-next-arch/lib/features/machine_details/presentation/pages/machine_details_page.dart
2026-08-10 16:26:15 +08:00

832 lines
36 KiB
Dart
Raw 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/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart';
import 'package:maibu_satabot_v2/core/localization/app_localizations.dart';
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
import 'package:maibu_satabot_v2/core/router/route_paths.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_state.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_bloc.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_state.dart';
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_cubit.dart'; // 🔥 添加 RemoteControlCubit
import 'package:maibu_satabot_v2/features/remote_control/presentation/widgets/webrtc/webrtc_local_player.dart';
class MachineDetailsPage extends StatefulWidget {
final DeviceEntity? device;
const MachineDetailsPage({super.key, this.device});
@override
State<MachineDetailsPage> createState() => _MachineDetailsPageState();
}
class _MachineDetailsPageState extends State<MachineDetailsPage> {
late TextEditingController _nameController;
late String _deviceName;
late String _deviceId;
BuildContext? _dialogContext;
BuildContext? _editNameSheetContext;
BuildContext? _loadingDialogContext; // 解绑加载弹窗
BuildContext? _updateLoadingContext; // 修改名称加载弹窗
bool _isUpdatingName = false;
// 🔥 保存 stream 引用,避免每次 rebuild 都重新获取
late Stream<DeviceStatusState> _deviceStatusStream;
late DeviceStatusState _initialState;
// 🔥 新增:视频相关状态
String _videoStreamUrl = '';
int _currentViewIndex = 0; // 0=前, 1=后, 2=左, 3=右, 4=上
// 视角配置
final List<Map<String, dynamic>> _viewConfigs = [
{'name': '前视', 'alignment': Alignment.topLeft, 'icon': Icons.arrow_upward},
{'name': '后视', 'alignment': Alignment.topRight, 'icon': Icons.arrow_downward},
{'name': '左视', 'alignment': Alignment.bottomLeft, 'icon': Icons.arrow_back},
{'name': '右视', 'alignment': Alignment.bottomRight, 'icon': Icons.arrow_forward},
{'name': '全景', 'alignment': Alignment.center, 'icon': Icons.view_agenda},
];
@override
void initState() {
super.initState();
_deviceName = (widget.device?.deviceAlias?.trim().isEmpty ?? true) ? '未知设备' : widget.device!.deviceAlias!;
_deviceId = widget.device?.deviceName ?? '';
_nameController = TextEditingController(text: _deviceName);
// 🔥 在 initState 中保存 stream 和 initial state
final bloc = context.read<DeviceStatusBloc>();
_deviceStatusStream = bloc.stream;
_initialState = bloc.state;
debugPrint('📦 [MachineDetails] initState - 初始状态: ${_initialState.runtimeType}');
// 🔥 初始化视频 URL
_initVideoUrl();
}
/// 🔥 初始化视频流 URL
void _initVideoUrl() {
final userState = context.read<AppUserCubit>().state;
debugPrint('🎬 [MachineDetails] 开始初始化视频URL');
debugPrint('🎬 [MachineDetails] deviceId: $_deviceId');
debugPrint('🎬 [MachineDetails] user: ${userState.user}');
debugPrint('🎬 [MachineDetails] token: ${userState.user?.token}');
if (_deviceId.isNotEmpty && userState.user != null && userState.user!.token != null) {
setState(() {
_videoStreamUrl = "webrtc://${TCPConsts.TCP_IP}/live/livestream/$_deviceId?token=${userState.user!.token}";
});
debugPrint('✅ [MachineDetails] 视频URL初始化成功: $_videoStreamUrl');
} else {
debugPrint('❌ [MachineDetails] 视频URL初始化失败');
debugPrint(' - deviceId.isEmpty: ${_deviceId.isEmpty}');
debugPrint(' - user == null: ${userState.user == null}');
debugPrint(' - token == null: ${userState.user?.token == null}');
}
}
@override
void dispose() {
_nameController.dispose();
// 兜底关闭所有弹窗
if (_loadingDialogContext != null && Navigator.canPop(_loadingDialogContext!)) {
Navigator.pop(_loadingDialogContext!);
}
if (_updateLoadingContext != null && Navigator.canPop(_updateLoadingContext!)) {
Navigator.pop(_updateLoadingContext!);
}
if (_dialogContext != null && Navigator.canPop(_dialogContext!)) {
Navigator.pop(_dialogContext!);
}
if (_editNameSheetContext != null && Navigator.canPop(_editNameSheetContext!)) {
Navigator.pop(_editNameSheetContext!);
}
super.dispose();
}
/// 弹出修改设备名称底部抽屉(保持不变)
void _showEditNameSheet() {
_nameController.text = _deviceName;
Future<void> bottomSheetFuture = showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
builder: (ctx) {
_editNameSheetContext = ctx;
return Padding(
padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(AppLocalizations.of(context).translate('machine_details.edit_name_title'), style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)),
const SizedBox(height: 16),
TextField(
controller: _nameController,
autofocus: true,
maxLength: 20,
decoration: InputDecoration(hintText: AppLocalizations.of(ctx).translate('machine_details.edit_name_hint'), border: const OutlineInputBorder(), counterText: ""),
),
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
_isUpdatingName = false;
Navigator.pop(ctx);
},
child: Text(AppLocalizations.of(context).translate('common.cancel')),
),
),
const SizedBox(width: 12),
Expanded(
child: OutlinedButton(
onPressed: _isUpdatingName
? null
: () async {
final text = _nameController.text.trim();
if (text.isEmpty) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.name_empty_error'))));
}
return;
}
if (text == _deviceName) {
Navigator.pop(ctx);
return;
}
setState(() => _isUpdatingName = true);
await _executeUpdateDeviceName(text);
},
child: _isUpdatingName
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white))
: Text(AppLocalizations.of(context).translate('common.confirm')),
),
),
],
),
],
),
);
},
);
bottomSheetFuture.whenComplete(() {
setState(() {
_isUpdatingName = false;
_editNameSheetContext = null;
});
});
}
@override
Widget build(BuildContext context) {
if (widget.device == null) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
appBar: AppBar(
title: Text(AppLocalizations.of(context).translate('machine_details.title')),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
// 优化1:先刷新列表,再返回(保证刷新逻辑执行)
// 核心:返回上一页
Navigator.pop(context);
if (context.mounted) {
final username = context.read<AppUserCubit>().state.user?.username ?? "";
// 🔥 修复:检查是否已有 targetDevice
final remoteControlState = context.read<RemoteControlCubit>().state;
if (remoteControlState.targetDevice == null) {
debugPrint('🔄 [MachineDetails] 无 targetDevice,开始加载设备列表');
context.read<DevicesCubit>().fetchAllDevices(username);
} else {
debugPrint('✅ [MachineDetails] 已有 targetDevice: ${remoteControlState.targetDevice!.deviceName},跳过加载');
}
}
},
),
),
body: Center(child: Text(AppLocalizations.of(context).translate('machine_details.device_info_error'))),
);
}
final currentDevice = widget.device!;
return BlocListener<DevicesCubit, DevicesState>(
listener: (context, state) {
if (state.operationType == DeviceOperationType.unbind) {
_handleUnbindResult(state);
}
if (state.operationType == DeviceOperationType.updateName) {
_handleUpdateNameResult(state);
}
},
child: Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
appBar: AppBar(
centerTitle: true,
title: Text(AppLocalizations.of(context).translate('machine_details.title'), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.pop(context);
if (context.mounted) {
final username = context.read<AppUserCubit>().state.user?.username ?? "";
// 🔥 修复:检查是否已有 targetDevice
final remoteControlState = context.read<RemoteControlCubit>().state;
if (remoteControlState.targetDevice == null) {
debugPrint('🔄 [MachineDetails AppBar] 无 targetDevice,开始加载设备列表');
context.read<DevicesCubit>().fetchAllDevices(username);
} else {
debugPrint('✅ [MachineDetails AppBar] 已有 targetDevice: ${remoteControlState.targetDevice!.deviceName},跳过加载');
}
}
},
),
elevation: 1,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
_buildDeviceStatusCard(currentDevice),
const SizedBox(height: 16),
// 🔥 新增:实时状态卡片
_buildRealTimeStatusCard(),
const SizedBox(height: 16),
_buildBasicInfoCard(context, currentDevice),
const SizedBox(height: 16),
_buildResourceCenterCard(),
const SizedBox(height: 24),
_buildUnbindButton(context),
],
),
),
),
);
}
/// 🔥 新增:实时状态卡片(显示控制模式、电压、电量)
Widget _buildRealTimeStatusCard() {
return StreamBuilder<DeviceStatusState>(
stream: _deviceStatusStream,
initialData: _initialState,
builder: (context, snapshot) {
final blocState = snapshot.data;
debugPrint('🔍 [MachineDetails] StreamBuilder 收到状态: ${blocState?.runtimeType}');
String controlMode = AppLocalizations.of(context).translate('common.unknown');
String voltage = '--';
String battery = '--';
if (blocState is DeviceStatusUpdated) {
debugPrint('✅ [MachineDetails] 有实时数据 - 电压:${blocState.status.voltage}, 电量:${blocState.status.battery}');
controlMode = blocState.status.controlMode == '3'
? AppLocalizations.of(context).translate('machine_details.remote_mode')
: AppLocalizations.of(context).translate('machine_details.local_mode');
voltage = '${blocState.status.voltage}V';
battery = '${blocState.status.battery}%';
} else {
debugPrint('⚠️ [MachineDetails] 没有实时数据,状态类型: ${blocState?.runtimeType}');
}
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Text(AppLocalizations.of(context).translate('machine_details.real_time_status'), style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
),
_buildStatusRow(AppLocalizations.of(context).translate('machine_details.control_mode'), controlMode),
const Divider(height: 24, color: Color(0xFFF0F0F0)),
_buildStatusRow(AppLocalizations.of(context).translate('machine_details.battery_voltage'), voltage),
const Divider(height: 24, color: Color(0xFFF0F0F0)),
_buildStatusRow(AppLocalizations.of(context).translate('machine_details.remaining_battery'), battery),
],
),
);
},
);
}
Widget _buildStatusRow(String label, String value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontSize: 16, color: Color(0xFF333333))),
Text(value, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF007AFF))),
],
);
}
/// 设备状态卡片(🔥 修改为视频展示 + 视角切换)
Widget _buildDeviceStatusCard(DeviceEntity device) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2))],
),
child: Column(
children: [
// 🔥 视频展示区域
AspectRatio(
aspectRatio: 16 / 9,
child: _videoStreamUrl.isNotEmpty
? WebRTCLocalPlayer(
streamUrl: _videoStreamUrl,
showLeftPip: false, // 不显示悬浮小窗
showRightPip: false,
mainViewAlignment: _viewConfigs[_currentViewIndex]['alignment'] as Alignment, // 🔥 根据视角切换画面
)
: Container(
color: Colors.black87,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.videocam_off, size: 64, color: Colors.white54),
const SizedBox(height: 16),
Text(
'视频未加载',
style: TextStyle(color: Colors.white70, fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'deviceId: $_deviceId',
style: TextStyle(color: Colors.white54, fontSize: 12),
),
const SizedBox(height: 4),
Text(
'URL: ${_videoStreamUrl.isEmpty ? "空" : "已设置"}',
style: TextStyle(color: Colors.white54, fontSize: 12),
),
],
),
),
),
),
// 🔥 在线状态标签
Padding(
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: device.isOnline ? const Color(0xFF00C853).withOpacity(0.1) : const Color(0xFF999999).withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
device.isOnline ? AppLocalizations.of(context).translate('home.device_online') : AppLocalizations.of(context).translate('home.device_offline'),
style: TextStyle(fontSize: 14, color: device.isOnline ? const Color(0xFF00C853) : const Color(0xFF999999)),
),
),
],
),
),
// 🔥 视角切换按钮
Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _viewConfigs.asMap().entries.map((entry) {
final index = entry.key;
final config = entry.value;
final isSelected = _currentViewIndex == index;
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: ElevatedButton(
onPressed: () {
setState(() {
_currentViewIndex = index;
});
debugPrint('🎬 [MachineDetails] 切换视角: ${config['name']}');
},
style: ElevatedButton.styleFrom(
backgroundColor: isSelected ? const Color(0xFF165DFF) : Colors.grey[200],
foregroundColor: isSelected ? Colors.white : Colors.grey[700],
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
minimumSize: const Size(0, 36),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(config['icon'] as IconData, size: 16),
const SizedBox(height: 2),
Text(
config['name'] as String,
style: const TextStyle(fontSize: 10),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
}).toList(),
),
),
],
),
);
}
/// 基本信息卡片(保持不变)
Widget _buildBasicInfoCard(BuildContext context, DeviceEntity device) {
final deviceId = device.deviceName ?? AppLocalizations.of(context).translate('common.unknown');
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(AppLocalizations.of(context).translate('machine_details.basic_info'), style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
),
_buildInfoRow(
context: context,
icon: Icons.info_outline,
label: AppLocalizations.of(context).translate('machine_details.device_name'),
trailing: GestureDetector(
onTap: _isUpdatingName ? null : _showEditNameSheet,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(_deviceName, style: const TextStyle(fontSize: 16)),
const SizedBox(width: 8),
const Icon(Icons.edit, size: 16, color: Color(0xFF007AFF)),
],
),
),
),
const Divider(height: 32, color: Color(0xFFF0F0F0)),
_buildInfoRow(
context: context,
icon: Icons.shield_outlined,
label: AppLocalizations.of(context).translate('machine_details.device_id'),
trailing: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: MediaQuery.of(context).size.width - 180,
child: GestureDetector(
onLongPress: () => _copyToClipboard(deviceId, AppLocalizations.of(context).translate('machine_details.copied')),
child: Text(
deviceId,
textAlign: TextAlign.right,
softWrap: true,
overflow: TextOverflow.visible,
style: const TextStyle(fontSize: 14, color: Colors.black87),
),
),
),
const SizedBox(height: 4),
GestureDetector(
onTap: () => _copyToClipboard(deviceId, AppLocalizations.of(context).translate('machine_details.device_id') + AppLocalizations.of(context).translate('machine_details.copied')),
child: const Icon(Icons.copy, size: 16, color: Color(0xFF999999)),
),
],
),
),
],
),
);
}
/// 资源中心卡片(保持不变)
Widget _buildResourceCenterCard() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(AppLocalizations.of(context).translate('machine_details.resource_center'), style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
),
_buildResourceItem(AppLocalizations.of(context).translate('machine_details.user_manual'), Icons.description_outlined),
const Divider(height: 32, color: Color(0xFFF0F0F0)),
_buildResourceItem(AppLocalizations.of(context).translate('machine_details.product_manual'), Icons.book_outlined),
],
),
);
}
/// 通用信息行组件(保持不变)
Widget _buildInfoRow({required BuildContext context, required IconData icon, required String label, required Widget trailing}) {
return Row(
children: [
Icon(icon, size: 18, color: const Color(0xFF999999)),
const SizedBox(width: 12),
Text(label, style: const TextStyle(fontSize: 16)),
const Spacer(),
trailing,
],
);
}
/// 资源项组件(保持不变)
Widget _buildResourceItem(String title, IconData icon) {
return InkWell(
onTap: () {
final loc = AppLocalizations.of(context);
if (title == loc.translate('machine_details.user_manual')) {
context.push(RoutePaths.usage);
} else {
context.push(RoutePaths.productDesc);
}
},
child: Row(
children: [
Icon(icon, size: 18, color: const Color(0xFF007AFF)),
const SizedBox(width: 12),
Text(title, style: const TextStyle(fontSize: 16)),
const Spacer(),
const Icon(Icons.arrow_forward_ios, size: 16, color: Color(0xFF999999)),
],
),
);
}
/// 解绑设备按钮(保持不变)
Widget _buildUnbindButton(BuildContext context) {
return SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: const Color(0xFFFF3B30),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
side: const BorderSide(color: Color(0xFFFF3B30), width: 1),
padding: const EdgeInsets.symmetric(vertical: 8),
textStyle: const TextStyle(height: 1.2),
),
onPressed: _isUpdatingName ? null : () => _showUnbindConfirmDialog(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.unarchive_outlined, size: 18),
const SizedBox(width: 8),
Text(AppLocalizations.of(context).translate('machine_details.unbind'), style: const TextStyle(fontSize: 16, height: 1.0), overflow: TextOverflow.visible),
],
),
),
);
}
/// 显示解绑确认弹窗(保持不变)
void _showUnbindConfirmDialog() {
showDialog(
context: context,
builder: (dialogContext) {
_dialogContext = dialogContext;
return AlertDialog(
title: Text(AppLocalizations.of(dialogContext).translate('machine_details.unbind_confirm_title')),
content: Text(AppLocalizations.of(dialogContext).translate('machine_details.unbind_confirm_content').replaceAll('%s', _deviceName)),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
actions: [
TextButton(onPressed: () => Navigator.pop(dialogContext), child: Text(AppLocalizations.of(dialogContext).translate('common.cancel'))),
TextButton(
style: TextButton.styleFrom(foregroundColor: const Color(0xFFFF3B30)),
onPressed: _executeUnbind,
child: Text(AppLocalizations.of(dialogContext).translate('common.confirm')),
),
],
);
},
);
}
Future<void> _executeUnbind() async {
if (_deviceId.isEmpty || _deviceName.isEmpty) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.device_info_error')), backgroundColor: Colors.red));
}
return;
}
// 关闭确认弹窗
if (_dialogContext != null && Navigator.canPop(_dialogContext!)) {
Navigator.pop(_dialogContext!);
}
try {
// 显示加载弹窗并记录上下文
if (context.mounted) {
showDialog(
context: context,
barrierDismissible: false,
builder: (ctx) {
_loadingDialogContext = ctx;
return AlertDialog(content: Row(children: [const CircularProgressIndicator(strokeWidth: 2), const SizedBox(width: 16), Text(AppLocalizations.of(ctx).translate('machine_details.unbinding_device'))]));
},
);
}
// 调用Cubit解绑方法
await context.read<DevicesCubit>().unbindDevice(_deviceId, _deviceName);
// 主动关闭加载弹窗
if (_loadingDialogContext != null && Navigator.canPop(_loadingDialogContext!)) {
Navigator.pop(_loadingDialogContext!);
_loadingDialogContext = null;
}
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.unbind_exception').replaceAll('%s', e.toString())), backgroundColor: Colors.red));
// 关闭加载弹窗
if (_loadingDialogContext != null && Navigator.canPop(_loadingDialogContext!)) {
Navigator.pop(_loadingDialogContext!);
_loadingDialogContext = null;
}
}
}
}
/// 执行修改设备名称(核心修改:新增兜底逻辑)
Future<void> _executeUpdateDeviceName(String newDeviceName) async {
// 基础校验
if (_deviceId.isEmpty || newDeviceName.isEmpty) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.device_info_error')), backgroundColor: Colors.red));
}
setState(() => _isUpdatingName = false);
return;
}
try {
// 显示加载弹窗并记录专属上下文
if (context.mounted) {
showDialog(
context: context,
barrierDismissible: false,
builder: (ctx) {
_updateLoadingContext = ctx;
return AlertDialog(content: Row(children: [const CircularProgressIndicator(strokeWidth: 2), const SizedBox(width: 16), Text(AppLocalizations.of(ctx).translate('machine_details.updating_name'))]));
},
);
}
//print("调用updateDeviceName,deviceId: $_deviceId, newDeviceName: $newDeviceName");
// 调用Cubit修改方法
await context.read<DevicesCubit>().updateDeviceName(_deviceId, newDeviceName);
// 主动关闭修改名称加载弹窗
if (_updateLoadingContext != null && Navigator.canPop(_updateLoadingContext!)) {
Navigator.pop(_updateLoadingContext!);
_updateLoadingContext = null;
}
// 主动重置更新状态
setState(() => _isUpdatingName = false);
// 兜底:强制将Cubit的isLoading置为false
if (context.mounted) {
context.read<DevicesCubit>().emit(context.read<DevicesCubit>().state.copyWith(isLoading: false, operationType: DeviceOperationType.none));
}
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.update_exception').replaceAll('%s', e.toString())), backgroundColor: Colors.red));
// 关闭修改名称加载弹窗
if (_updateLoadingContext != null && Navigator.canPop(_updateLoadingContext!)) {
Navigator.pop(_updateLoadingContext!);
_updateLoadingContext = null;
}
}
setState(() => _isUpdatingName = false);
// 兜底:强制重置Cubit的loading状态
if (context.mounted) {
context.read<DevicesCubit>().emit(context.read<DevicesCubit>().state.copyWith(isLoading: false, operationType: DeviceOperationType.none));
}
}
}
/// 处理解绑结果(保持不变)
void _handleUnbindResult(DevicesState state) {
// 强制关闭加载弹窗
if (_loadingDialogContext != null && Navigator.canPop(_loadingDialogContext!)) {
Navigator.pop(_loadingDialogContext!);
_loadingDialogContext = null;
} else if (!state.isLoading && context.mounted && Navigator.canPop(context)) {
Navigator.pop(context);
}
if (state.errorMessage?.isEmpty == true && !state.isLoading) {
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.unbind_success').replaceAll('%s', _deviceName)), backgroundColor: Colors.green, duration: const Duration(seconds: 2)));
final username = context.read<AppUserCubit>().state.user?.username ?? "";
// 🔥 修复:检查是否已有 targetDevice
final remoteControlState = context.read<RemoteControlCubit>().state;
if (remoteControlState.targetDevice == null) {
debugPrint('🔄 [MachineDetails Unbind] 无 targetDevice,开始加载设备列表');
context.read<DevicesCubit>().fetchAllDevices(username);
} else {
debugPrint('✅ [MachineDetails Unbind] 已有 targetDevice: ${remoteControlState.targetDevice!.deviceName},跳过加载');
}
}
} else if (state.errorMessage?.isNotEmpty == true && !state.isLoading) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.unbind_failed').replaceAll('%s', state.errorMessage ?? AppLocalizations.of(context).translate('common.unknown'))), backgroundColor: Colors.red));
}
}
context.go(RoutePaths.home);
}
/// 处理修改名称结果(核心修改:放宽判断条件)
void _handleUpdateNameResult(DevicesState state) {
// 强制关闭修改名称加载弹窗(优先用专属上下文)
if (_updateLoadingContext != null && Navigator.canPop(_updateLoadingContext!)) {
Navigator.pop(_updateLoadingContext!);
_updateLoadingContext = null;
}
// 兜底:兼容旧逻辑
else if (context.mounted && Navigator.canPop(context)) {
// 不管loading状态,只要弹窗存在就关闭
Navigator.pop(context);
}
// 处理结果:放宽条件,只要无错误信息就认为成功(不管loading)
// print(
// "处理修改名称结果,operationType: ${state.operationType}, isLoading: ${state.isLoading}, errorMessage: ${state.errorMessage},state.errorMessage?.isEmpty: ${state.errorMessage?.isEmpty}",
// );
// 核心修改:去掉!state.isLoading的判断
if (state.errorMessage?.isEmpty == true) {
setState(() {
_deviceName = _nameController.text.trim();
_isUpdatingName = false; // 双重保障重置状态
});
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.name_updated_success').replaceAll('%s', _deviceName)), backgroundColor: Colors.green, duration: const Duration(seconds: 2)));
// 关闭修改名称的底部抽屉(如果还在)
if (_editNameSheetContext != null && Navigator.canPop(_editNameSheetContext!)) {
Navigator.pop(_editNameSheetContext!);
_editNameSheetContext = null;
}
// 强制重置Cubit的loading状态
context.read<DevicesCubit>().emit(state.copyWith(isLoading: false, operationType: DeviceOperationType.none));
}
} else if (state.errorMessage?.isNotEmpty == true) {
setState(() => _isUpdatingName = false);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).translate('machine_details.update_failed').replaceAll('%s', state.errorMessage ?? AppLocalizations.of(context).translate('common.unknown'))), backgroundColor: Colors.red));
// 强制重置Cubit的loading状态
context.read<DevicesCubit>().emit(state.copyWith(isLoading: false, operationType: DeviceOperationType.none));
}
}
}
/// 复制到剪贴板(保持不变)
void _copyToClipboard(String text, String tip) {
Clipboard.setData(ClipboardData(text: text));
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(tip), duration: const Duration(seconds: 1)));
}
}
}