完成扫码绑定的cubit层

完成扫码依赖的下载和权限配置
完成扫码绑定设备-初步测试打印扫码结果
This commit is contained in:
2026-03-04 10:22:12 +08:00
parent cb09c21f1b
commit 1bbcd451da
10 changed files with 134 additions and 6 deletions

View File

@@ -12,6 +12,10 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- 关键! -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 如果还需要扫描闪光灯控制,可选添加 -->
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />

View File

@@ -49,7 +49,8 @@
<string>需要访问您的位置以显示地图</string>
<key>NSCameraUsageDescription</key>
<string>需要访问相机以扫描二维码</string>
<string>我们需要访问您的摄像头以扫描二维码绑定设备</string>
</dict>
</plist>

View File

@@ -35,6 +35,7 @@ import '../../features/devices/data/repositories/route_planning_repository_impl.
import '../../features/devices/domain/repositories/device_hostrity_work_repository.dart';
import '../../features/devices/domain/repositories/path_repository.dart';
import '../../features/devices/domain/repositories/route_planning_repository.dart';
import '../../features/devices/domain/usecases/bind_device_usecase.dart';
import '../../features/devices/domain/usecases/delete_work_record_usecase.dart';
import '../../features/devices/domain/usecases/device_work_hostrirty_usecase.dart';
import '../../features/devices/domain/usecases/generate_path_usecase.dart';
@@ -130,6 +131,7 @@ Future<void> init() async {
sl.registerLazySingleton(() => DeviceWorkHostrirty(sl()));
sl.registerLazySingleton<SelectWorkRecordUseCase>(() => SelectWorkRecordUseCase(sl<PathRepository>()));
sl.registerLazySingleton(() => UnbindDeviceUseCase(sl()));
sl.registerLazySingleton(() => BindDeviceUseCase(sl()));
sl.registerLazySingleton(() => UpdateDevicenameUsecase(sl()));
sl.registerLazySingleton<SaveWorkRecordUseCase>(() => SaveWorkRecordUseCase(sl<PathRepository>()));
sl.registerLazySingleton<SwitchDeviceUseCase>(() => SwitchDeviceUseCase(sl()));
@@ -148,7 +150,7 @@ Future<void> init() async {
sl.registerLazySingleton(() => AppUserCubit()); // AuthCubit 依赖它,必须先注册
sl.registerLazySingleton(() => GetDeviceLocationUseCase(sl()));
sl.registerLazySingleton(
() => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl(),sl()),
() => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl(),sl(), sl()),
);
sl.registerFactory(() => RemoteControlCubit(sl()));
/* // 工厂模式(留存,每次获取新实例)

View File

@@ -30,7 +30,7 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
// 🔥 新增:模拟测试数据(调试用)
_testMockData();
// _testMockData();
}
void _testMockData() {
// 用户提供的真实指令数据(字节数组)

View File

@@ -13,6 +13,7 @@ import 'package:maibu_satabot_v2/features/devices/domain/usecases/update_devicen
import '../../data/models/device_add_path_point_model.dart';
import '../../data/models/device_work_area_param_model.dart';
import '../../domain/usecases/bind_device_usecase.dart';
import '../../domain/usecases/delete_work_record_usecase.dart';
import '../../domain/usecases/get_device_location_usecase.dart';
import '../../domain/usecases/get_work_record_usecase.dart';
@@ -25,6 +26,7 @@ class DevicesCubit extends Cubit<DevicesState> {
final DeviceRepository repository;
final GetWorkRecordUseCase _getWorkRecordUseCase;
final DeleteWorkRecordUseCase _deleteWorkRecordUseCase;
final BindDeviceUseCase _bindDeviceUseCase;
final UnbindDeviceUseCase _unbindDeviceUseCase;
final UpdateDevicenameUsecase _updateDevicename;
final SelectWorkRecordUseCase _selectWorkRecordUseCase;
@@ -43,7 +45,7 @@ class DevicesCubit extends Cubit<DevicesState> {
this._selectWorkRecordUseCase,
this._saveWorkRecordUseCase,
this._generatePathUseCase,
this._routePlanningUseCase
this._routePlanningUseCase, this._bindDeviceUseCase
) : super(const DevicesState());
Future<void> unbindDevice(String deviceId, String deviceName) async {
@@ -176,6 +178,41 @@ class DevicesCubit extends Cubit<DevicesState> {
selectDevice(device);
}, (r) => emit(state.copyWith(isLoading: false, selectedDevice: device)));
}
/// 绑定设备
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 ?? '绑定设备失败',
)),
// 成功处理(返回 int 状态码)
(successCode) {
if (successCode == 1) {
emit(state.copyWith(isLoading: false, errorMessage: ''));
// 绑定成功后刷新设备列表
//fetchAllDevices(state.?.username ?? '');
} else {
emit(state.copyWith(
isLoading: false,
errorMessage: '绑定失败:状态码 $successCode',
));
}
},
);
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: '绑定异常:${e.toString()}',
));
}
}
/// 获取设备位置
Future<void> getDeviceLocation(DeviceEntity device) async {

View File

@@ -10,6 +10,7 @@ import '../../../auth/presentation/bloc/auth_state.dart';
import '../../../auth/presentation/pages/login_page.dart';
import '../../../web_view/web_view_page.dart';
import '../web_view/user_info_pages.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class MyPage extends StatelessWidget {
const MyPage({super.key});

View File

@@ -1,5 +1,10 @@
// user_info_card.dart
import 'package:flutter/material.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';
class UserInfoCard extends StatelessWidget {
final String userName;
@@ -28,7 +33,7 @@ class UserInfoCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(userName, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text('用户id:$userId', style: TextStyle(fontSize: 12, color: Colors.grey)),
Text('用户 id:$userId', style: TextStyle(fontSize: 12, color: Colors.grey)),
],
),
),
@@ -39,9 +44,75 @@ class UserInfoCard extends StatelessWidget {
Spacer(),
IconButton(
icon: Icon(Icons.add),
onPressed: () {},
// 3. 绑定点击事件
onPressed: () => _handleScanAndBind(context),
),
],
);
}
// 4. 定义扫码与绑定逻辑
Future<void> _handleScanAndBind(BuildContext context) async {
// 跳转到扫码页面
final String? qrCode = 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);
}
}
},
),
),
);
// 如果用户取消或未扫到码,直接返回
if (qrCode == null || qrCode.isEmpty) {
return;
}
// 显示加载对话框
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => Center(child: CircularProgressIndicator()),
);
try {
// 5. 获取 Cubit 实例并调用绑定接口
// 假设 AuthCubit 中已定义 bindDeviceByQr 方法
final Cubit = GetIt.I<DevicesCubit>();
// 执行绑定逻辑 (请确保 Cubit 层已实现此方法)
//await Cubit.bindDevice(qrCode);
// 关闭加载框
if (context.mounted) Navigator.pop(context);
// 成功提示
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('设备绑定成功!'), backgroundColor: Colors.green),
);
}
} catch (e) {
// 关闭加载框
if (context.mounted) Navigator.pop(context);
// 失败提示
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('绑定失败:$e'), backgroundColor: Colors.red),
);
}
}
}
}

View File

@@ -10,6 +10,7 @@ import file_selector_macos
import flutter_webrtc
import geolocator_apple
import isar_community_flutter_libs
import mobile_scanner
import package_info_plus
import sentry_flutter
import shared_preferences_foundation
@@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@@ -799,6 +799,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
mobile_scanner:
dependency: "direct dev"
description:
name: mobile_scanner
sha256: "1b60b8f9d4ce0cb0e7d7bc223c955d083a0737bee66fa1fcfe5de48225e0d5b3"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.5.7"
native_toolchain_c:
dependency: transitive
description:

View File

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