diff --git a/airhub_app/ios/Flutter/AppFrameworkInfo.plist b/airhub_app/ios/Flutter/AppFrameworkInfo.plist
index 1dc6cf7..391a902 100644
--- a/airhub_app/ios/Flutter/AppFrameworkInfo.plist
+++ b/airhub_app/ios/Flutter/AppFrameworkInfo.plist
@@ -20,7 +20,5 @@
????
CFBundleVersion
1.0
- MinimumOSVersion
- 13.0
diff --git a/airhub_app/ios/Runner/AppDelegate.swift b/airhub_app/ios/Runner/AppDelegate.swift
index 6266644..c30b367 100644
--- a/airhub_app/ios/Runner/AppDelegate.swift
+++ b/airhub_app/ios/Runner/AppDelegate.swift
@@ -2,12 +2,15 @@ import Flutter
import UIKit
@main
-@objc class AppDelegate: FlutterAppDelegate {
+@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+
+ func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
+ GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
+ }
}
diff --git a/airhub_app/ios/Runner/Info.plist b/airhub_app/ios/Runner/Info.plist
index 3083e79..943fe1e 100644
--- a/airhub_app/ios/Runner/Info.plist
+++ b/airhub_app/ios/Runner/Info.plist
@@ -2,6 +2,8 @@
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -24,6 +26,42 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ NSBluetoothAlwaysUsageDescription
+ 需要蓝牙权限来搜索和连接您的设备
+ NSBluetoothPeripheralUsageDescription
+ 需要蓝牙权限来搜索和连接您的设备
+ NSLocationWhenInUseUsageDescription
+ 需要位置权限以扫描附近的蓝牙设备
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ flutter
+ UISceneDelegateClassName
+ FlutterSceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
+ UIApplicationSupportsIndirectInputEvents
+
+ UILaunchScreen
+
+ UIColorName
+ LaunchBackgroundColor
+ UIImageName
+ LaunchImage
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,22 +79,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
- NSBluetoothAlwaysUsageDescription
- 需要蓝牙权限来搜索和连接您的设备
- NSBluetoothPeripheralUsageDescription
- 需要蓝牙权限来搜索和连接您的设备
- NSLocationWhenInUseUsageDescription
- 需要位置权限以扫描附近的蓝牙设备
- UILaunchScreen
-
- UIColorName
- LaunchBackgroundColor
- UIImageName
- LaunchImage
-
diff --git a/airhub_app/lib/core/router/app_router.dart b/airhub_app/lib/core/router/app_router.dart
index fe4a7df..95fdfd2 100644
--- a/airhub_app/lib/core/router/app_router.dart
+++ b/airhub_app/lib/core/router/app_router.dart
@@ -5,6 +5,7 @@ import '../../features/auth/presentation/pages/login_page.dart';
import '../../pages/bluetooth_page.dart';
import '../../pages/device_control_page.dart';
import '../../pages/home_page.dart';
+import '../../pages/product_selection_page.dart';
import '../../pages/profile/profile_page.dart';
import '../../pages/webview_page.dart';
import '../../pages/wifi_config_page.dart';
@@ -48,6 +49,10 @@ GoRouter goRouter(Ref ref) {
extra: state.extra as Map?,
),
),
+ GoRoute(
+ path: '/product-selection',
+ builder: (context, state) => const ProductSelectionPage(),
+ ),
GoRoute(
path: '/device-control',
builder: (context, state) => const DeviceControlPage(),
diff --git a/airhub_app/lib/core/router/app_router.g.dart b/airhub_app/lib/core/router/app_router.g.dart
index c2f3d9a..105a644 100644
--- a/airhub_app/lib/core/router/app_router.g.dart
+++ b/airhub_app/lib/core/router/app_router.g.dart
@@ -48,4 +48,4 @@ final class GoRouterProvider
}
}
-String _$goRouterHash() => r'8e620e452bb81f2c6ed87b136283a9e508dca2e9';
+String _$goRouterHash() => r'9f77a00bcbc90890c4b6594a9709288e5206c7d8';
diff --git a/airhub_app/lib/features/device/domain/entities/device.g.dart b/airhub_app/lib/features/device/domain/entities/device.g.dart
index 04c0e22..733476b 100644
--- a/airhub_app/lib/features/device/domain/entities/device.g.dart
+++ b/airhub_app/lib/features/device/domain/entities/device.g.dart
@@ -30,12 +30,12 @@ Map _$DeviceTypeToJson(_DeviceType instance) =>
_DeviceInfo _$DeviceInfoFromJson(Map json) => _DeviceInfo(
id: (json['id'] as num).toInt(),
sn: json['sn'] as String,
- deviceType: (json['device_type'] is Map)
- ? DeviceType.fromJson(json['device_type'] as Map)
- : null,
- deviceTypeInfo: (json['device_type_info'] is Map)
- ? DeviceType.fromJson(json['device_type_info'] as Map)
- : null,
+ deviceType: json['device_type'] == null
+ ? null
+ : DeviceType.fromJson(json['device_type'] as Map),
+ deviceTypeInfo: json['device_type_info'] == null
+ ? null
+ : DeviceType.fromJson(json['device_type_info'] as Map),
macAddress: json['mac_address'] as String?,
name: json['name'] as String? ?? '',
status: json['status'] as String? ?? 'in_stock',
diff --git a/airhub_app/lib/features/device/presentation/controllers/device_controller.dart b/airhub_app/lib/features/device/presentation/controllers/device_controller.dart
index 0f03c29..4009689 100644
--- a/airhub_app/lib/features/device/presentation/controllers/device_controller.dart
+++ b/airhub_app/lib/features/device/presentation/controllers/device_controller.dart
@@ -21,6 +21,7 @@ class DeviceController extends _$DeviceController {
Future bindDevice(String sn, {int? spiritId}) async {
final repository = ref.read(deviceRepositoryProvider);
final result = await repository.bindDevice(sn, spiritId: spiritId);
+ if (!ref.mounted) return false;
return result.fold(
(failure) => false,
(bindingId) {
@@ -33,6 +34,7 @@ class DeviceController extends _$DeviceController {
Future unbindDevice(int userDeviceId) async {
final repository = ref.read(deviceRepositoryProvider);
final result = await repository.unbindDevice(userDeviceId);
+ if (!ref.mounted) return false;
return result.fold(
(failure) => false,
(_) {
@@ -48,6 +50,7 @@ class DeviceController extends _$DeviceController {
Future updateSpirit(int userDeviceId, int spiritId) async {
final repository = ref.read(deviceRepositoryProvider);
final result = await repository.updateSpirit(userDeviceId, spiritId);
+ if (!ref.mounted) return false;
return result.fold(
(failure) => false,
(updated) {
@@ -78,6 +81,7 @@ class DeviceDetailController extends _$DeviceDetailController {
Future updateSettings(Map settings) async {
final repository = ref.read(deviceRepositoryProvider);
final result = await repository.updateSettings(userDeviceId, settings);
+ if (!ref.mounted) return false;
return result.fold(
(failure) => false,
(_) {
@@ -90,6 +94,7 @@ class DeviceDetailController extends _$DeviceDetailController {
Future configWifi(String ssid) async {
final repository = ref.read(deviceRepositoryProvider);
final result = await repository.configWifi(userDeviceId, ssid);
+ if (!ref.mounted) return false;
return result.fold(
(failure) => false,
(_) {
diff --git a/airhub_app/lib/features/device/presentation/controllers/device_controller.g.dart b/airhub_app/lib/features/device/presentation/controllers/device_controller.g.dart
index 05de583..84e11e3 100644
--- a/airhub_app/lib/features/device/presentation/controllers/device_controller.g.dart
+++ b/airhub_app/lib/features/device/presentation/controllers/device_controller.g.dart
@@ -36,7 +36,7 @@ final class DeviceControllerProvider
DeviceController create() => DeviceController();
}
-String _$deviceControllerHash() => r'9b39117bd54964ba0035aad0eca10250454efaa7';
+String _$deviceControllerHash() => r'3f73a13c7f93fecb9fe781efc4ee305b6186639e';
/// 管理用户设备列表
diff --git a/airhub_app/lib/pages/product_selection_page.dart b/airhub_app/lib/pages/product_selection_page.dart
index 28c0aa9..4688a0b 100644
--- a/airhub_app/lib/pages/product_selection_page.dart
+++ b/airhub_app/lib/pages/product_selection_page.dart
@@ -1,17 +1,20 @@
import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/animated_gradient_background.dart';
-import '../widgets/ios_toast.dart';
+import '../features/device/presentation/controllers/device_controller.dart';
+import '../features/device/domain/entities/device.dart';
-class ProductSelectionPage extends StatefulWidget {
+class ProductSelectionPage extends ConsumerStatefulWidget {
const ProductSelectionPage({super.key});
@override
- State createState() => _ProductSelectionPageState();
+ ConsumerState createState() => _ProductSelectionPageState();
}
-class _ProductSelectionPageState extends State {
+class _ProductSelectionPageState extends ConsumerState {
final ScrollController _scrollController = ScrollController();
@override
@@ -28,12 +31,30 @@ class _ProductSelectionPageState extends State {
super.dispose();
}
+ /// 产品 ID 到后端 product_code 的映射
+ static const Map> _productCodeMap = {
+ 'capybara': ['KPBL-ON'],
+ 'badge-ai': ['DZBJ-ON'],
+ 'badge-basic': ['DZBJ-OFF'],
+ };
+
+ /// 查找用户是否已绑定该产品类型的设备
+ UserDevice? _findBoundDevice(String productId, List devices) {
+ final codes = _productCodeMap[productId];
+ if (codes == null || codes.isEmpty) return null;
+ for (final device in devices) {
+ final dt = device.device.deviceType ?? device.device.deviceTypeInfo;
+ if (dt != null && codes.contains(dt.productCode)) {
+ return device;
+ }
+ }
+ return null;
+ }
+
static final List