完成扫码绑定设备-初步测试正确完成

This commit is contained in:
2026-03-04 17:36:28 +08:00
parent 786d11c936
commit 1cff8f9ee2
5 changed files with 157 additions and 58 deletions

View File

@@ -26,17 +26,43 @@ class DeviceHttpDatasourceImpl implements DeviceHttpDatasource {
@override
Future<int> bindDevice(String deviceId, String deviceAlias) async {
final logger = GetIt.I<ILoggerService>() as SentryLoggerImpl;
logger.logWithLevel('用户点击bindDevice方法开始触发', level: 'INFO');
logger.logWithLevel(
'用户点击bindDevice方法API 请求开始',
level: 'INFO',
data: {'url': 'https://serviceri.satabot.com/iot/device/bindDevice'}
);
var response = await dio.post(
HttpApiConsts.bindDevice,
data: {'deviceId': deviceId, 'deviceAlias': deviceAlias},
);
logger.logWithLevel(
'请求详细参数',
level: 'DEBUG',
data: {
'url': 'https://serviceri.satabot.com/iot/device/bindDevice',
'deviceId': deviceId,
'deviceAlias': deviceAlias
}
);
if (response.statusCode != 200) {
logger.logWithLevel(
'API 响应失败',
level: 'ERROR',
data: {'statusCode': response.statusCode, 'data': response.data}
);
throw Exception('网络请求失败:${response.statusCode}');
}
final responseData = response.data;
if (responseData['code'] != 200 || responseData['data'] != true) {
logger.logWithLevel(
'API 响应失败',
level: 'ERROR',
data: {'statusCode': response.statusCode, 'data': response.data}
);
throw Exception(responseData['msg'] ?? '业务异常');
}

View File

@@ -180,16 +180,21 @@ class DevicesCubit extends Cubit<DevicesState> {
}
/// 绑定设备
Future<void> bindDevice(String deviceId, String deviceAlias) async {
emit(state.copyWith(isLoading: true, errorMessage: ''));
try {
final params = BindDeviceParams(deviceId, deviceAlias);
final result = await _bindDeviceUseCase.call(params);
result.fold(
// 失败处理
(failure) => emit(state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '绑定设备失败',
)),
(failure) {
emit(state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '绑定设备失败',
));
// 抛出异常,携带后端返回的错误消息(如“设备不存在”)
throw Exception(failure.message ?? '绑定设备失败');
},
// 成功处理(返回 int 状态码)
(successCode) {
if (successCode == 1) {
@@ -201,14 +206,17 @@ class DevicesCubit extends Cubit<DevicesState> {
isLoading: false,
errorMessage: '绑定失败:状态码 $successCode',
));
throw Exception('绑定失败:状态码 $successCode');
}
},
);
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: '绑定异常:${e.toString()}',
));
// emit(state.copyWith(
// isLoading: false,
// errorMessage: '绑定异常:${e.toString()}',
// ));
// 🔥 重新抛出,传递给 UI
rethrow;
}
}

View File

@@ -1,10 +1,9 @@
// user_info_card.dart
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
import 'package:mobile_scanner/mobile_scanner.dart'; // 1. 导入扫码库
import 'package:get_it/get_it.dart'; // 用于获取 Cubit 实例
// 2. 导入你的 Cubit 文件,请根据实际路径调整
import '../../../auth/presentation/bloc/auth_cubit.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:get_it/get_it.dart';
// 其他导入保持不变...
class UserInfoCard extends StatelessWidget {
final String userName;
@@ -27,91 +26,157 @@ class UserInfoCard extends StatelessWidget {
),
),
),
SizedBox(width: 12),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(userName, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text('用户 id:$userId', style: TextStyle(fontSize: 12, color: Colors.grey)),
Text(userName, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text('用户 id:$userId', style: const TextStyle(fontSize: 12, color: Colors.grey)),
],
),
),
IconButton(
icon: Icon(Icons.edit),
icon: const Icon(Icons.edit),
onPressed: () {},
),
Spacer(),
const Spacer(),
IconButton(
icon: Icon(Icons.add),
// 3. 绑定点击事件
icon: const Icon(Icons.add),
onPressed: () => _handleScanAndBind(context),
),
],
);
}
// 4. 定义扫码与绑定逻辑
Future<void> _handleScanAndBind(BuildContext context) async {
// 跳转到扫码页面
final String? qrCode = await Navigator.push<String>(
bool isScanned = false;
// 1. 扫码逻辑 (保持不变)
final String? deviceCode = await Navigator.push<String>(
context,
MaterialPageRoute(
builder: (context) => MobileScanner(
onDetect: (capture) {
final List<Barcode> barcodes = capture.barcodes;
if (barcodes.isNotEmpty) {
debugPrint('识别到的数据: $barcodes');
final String? code = barcodes.first.rawValue;
if (code != null && code.isNotEmpty) {
// 识别成功,立即返回结果并关闭页面
debugPrint('扫码成功,识别到的数据: $code');
// Navigator.pop(context, code);
builder: (context) => Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
title: const Text('扫码绑定设备'),
backgroundColor: Colors.black54,
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
),
body: MobileScanner(
onDetect: (BarcodeCapture capture) {
if (isScanned) return;
final List<Barcode> barcodes = capture.barcodes;
if (barcodes.isNotEmpty) {
final String? code = barcodes.first.rawValue;
if (code != null && code.isNotEmpty) {
isScanned = true;
// 使用 addPostFrameCallback 确保在帧渲染后执行,避免锁死
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted && Navigator.canPop(context)) {
Navigator.pop(context, code);
}
});
}
}
}
},
},
),
),
),
);
// 如果用户取消或未扫到码,直接返回
if (qrCode == null || qrCode.isEmpty) {
if (deviceCode == null || deviceCode.isEmpty) {
return;
}
// 显示加载对话框
// 2. 确认对话框 (保持不变)
final bool? confirmBind = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: const Text('确认绑定设备'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('设备信息:'),
const SizedBox(height: 8),
Text('设备编码:$deviceCode'),
const Text('设备状态:未绑定'),
const Text('设备类型:智能设备'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('确定绑定'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
),
],
),
);
if (confirmBind != true) {
return;
}
// 3. 显示加载框
// 保存 BuildContext 引用,防止后续 context 变化导致找不到正确的 Navigator
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => Center(child: CircularProgressIndicator()),
builder: (loadingContext) => const Center(child: CircularProgressIndicator()),
);
try {
// 5. 获取 Cubit 实例并调用绑定接口
final Cubit = GetIt.I<DevicesCubit>();
final devicesCubit = GetIt.I<DevicesCubit>();
await devicesCubit.bindDevice(deviceCode, '');
// 执行绑定逻辑
//await Cubit.bindDevice(qrCode);
// 关闭加载框
if (context.mounted) Navigator.pop(context);
// 成功提示
if (context.mounted) {
// ✅ 成功:安全关闭加载框
if (context.mounted && Navigator.canPop(context)) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('设备绑定成功!'), backgroundColor: Colors.green),
const SnackBar(content: Text('设备绑定成功!'), backgroundColor: Colors.green),
);
}
} catch (e) {
// 关闭加载框
if (context.mounted) Navigator.pop(context);
// ✅ 失败:安全关闭加载框 (关键修复点)
// 即使发生了 Framework 错误,也要尝试关闭弹窗
if (context.mounted && Navigator.canPop(context)) {
Navigator.pop(context);
// 失败提示
if (context.mounted) {
// 显示具体错误信息
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('绑定失败:$e'), backgroundColor: Colors.red),
SnackBar(
content: Text('绑定失败:${e.toString()}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 4),
),
);
} else {
// 极端情况:如果 Navigator 已经锁死无法 pop,尝试用其他方式提示用户
// 或者等待下一帧再试
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
// 尝试再次关闭,或者只显示错误提示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('绑定失败:${e.toString()} (请手动返回)'),
backgroundColor: Colors.orange,
),
);
}
});
}
debugPrint('设备绑定失败详情:$e');
}
}
}

View File

@@ -811,10 +811,10 @@ packages:
dependency: "direct dev"
description:
name: mobile_scanner
sha256: "1b60b8f9d4ce0cb0e7d7bc223c955d083a0737bee66fa1fcfe5de48225e0d5b3"
sha256: "827765afbd4792ff3fd105ad593821ac0f6d8a7d352689013b07ee85be336312"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.5.7"
version: "4.0.1"
native_toolchain_c:
dependency: transitive
description:

View File

@@ -143,7 +143,7 @@ dev_dependencies:
flutter_launcher_icons: ^0.13.1
mobile_scanner: ^3.4.1 # 请根据实际需求选择最新版本
mobile_scanner: ^4.0.1 # 升级到最新稳定版
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec