diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index f1195f0fc..75a82d84d 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -59,6 +59,8 @@
193d0a2b1757c41cfa557ea8e8f21c33
LSApplicationQueriesSchemes
+ App-Prefs
+ prefs
weixin
weixinULAPI
weixinURLParamsAPI
@@ -127,12 +129,12 @@
LaunchScreen
UIMainStoryboardFile
Main
- UISupportedInterfaceOrientations~ipad
+ UISupportedInterfaceOrientations
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
diff --git a/lib/core/bindings/initial_binding.dart b/lib/core/bindings/initial_binding.dart
index 8c98da39d..0c61157c0 100644
--- a/lib/core/bindings/initial_binding.dart
+++ b/lib/core/bindings/initial_binding.dart
@@ -8,6 +8,7 @@ import '../../../data/services/navigation_manager.dart';
import 'package:get/get.dart';
import '../../data/services/meeting/meeting_task_service.dart';
import '../../data/services/meeting/meeting_upload_service.dart';
+import '../../data/services/novice_guide_service.dart';
import '../../data/services/speech_factory.dart';
import '../../data/services/volcano_translation_service.dart';
import '../../data/services/volcano_ai_service.dart';
@@ -68,6 +69,9 @@ class InitialBinding extends Bindings {
// qq音乐服务
Get.lazyPut(() => QqMusocService(), fenix: true);
+ // 新手引导
+ Get.lazyPut(() => NoviceGuideService(), fenix: true);
+
// 杰理蓝牙服务
// // 语言管理器(需要最先初始化)
diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart
index f529e2a7a..51d9bd764 100644
--- a/lib/core/translations/language/en_us.dart
+++ b/lib/core/translations/language/en_us.dart
@@ -813,6 +813,19 @@ const Map enUS = {
'deviceAuthorizationFailedCheckCode':
'Device authorization failed, please check authorization code',
+ ///iOS蓝牙引导窗
+ 'bluetoothGuideTitle': 'Manual Bluetooth Activation Required', // 需要手动开启蓝牙
+ 'bluetoothGuideSteps': 'Please follow these steps:', // 请按照以下步骤操作:
+ 'bluetoothGuideStep1':
+ 'Enable Bluetooth: Settings → Bluetooth → Turn On', // 开启蓝牙:设置 → 蓝牙 → 开启
+ 'bluetoothGuideStep2':
+ 'Connect Device: Find your device in "My Devices" and tap to connect', // 连接设备:在"我的设备"中找到您的设备并点击连接
+ 'bluetoothGuideStep3':
+ 'Return to app to continue pairing process', // 返回应用继续配对流程
+ 'bluetoothGuideTip':
+ 'Tip: You can also long press the Bluetooth icon in Control Center to quickly access Bluetooth settings', // 提示:也可以从控制中心长按蓝牙图标快速进入蓝牙设置
+ 'iKnow': 'I Know', // 我知道了
+
// 用户反馈页
"feedback": "Feedback",
'feedbackInfo': 'Provide feedback or suggestions',
diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart
index 52a3cc093..6bd1701d6 100644
--- a/lib/core/translations/language/zh_cn.dart
+++ b/lib/core/translations/language/zh_cn.dart
@@ -746,6 +746,15 @@ const Map zhCN = {
'bleConnectionFailed': 'BLE连接失败',
'deviceAuthorizationFailedCheckCode': '设备授权失败,请检查授权码',
+ ///iOS蓝牙引导窗
+ 'bluetoothGuideTitle': '需要手动开启蓝牙', // 需要手动开启蓝牙
+ 'bluetoothGuideSteps': '请按照以下步骤操作:', // 请按照以下步骤操作:
+ 'bluetoothGuideStep1': '开启蓝牙:设置 → 蓝牙 → 开启', // 开启蓝牙:设置 → 蓝牙 → 开启
+ 'bluetoothGuideStep2': '连接设备:在"我的设备"中找到您的设备并点击连接', // 连接设备:在"我的设备"中找到您的设备并点击连接
+ 'bluetoothGuideStep3': '返回应用继续配对流程', // 返回应用继续配对流程
+ 'bluetoothGuideTip': '提示:也可以从控制中心长按蓝牙图标快速进入蓝牙设置', // 提示:也可以从控制中心长按蓝牙图标快速进入蓝牙设置
+ 'iKnow': '我知道了', // 我知道了
+
//用户反馈页
"feedback": "用户反馈",
'feedbackInfo': '提供反馈或建议',
diff --git a/lib/data/services/novice_guide_service.dart b/lib/data/services/novice_guide_service.dart
new file mode 100644
index 000000000..aeb3baa39
--- /dev/null
+++ b/lib/data/services/novice_guide_service.dart
@@ -0,0 +1,34 @@
+import 'package:flutter_intro/flutter_intro.dart';
+import 'package:get/get.dart';
+import 'package:get_storage/get_storage.dart';
+
+class NoviceGuideService extends GetxService {
+ final GetStorage _storage = GetStorage();
+
+ bool isHomeGuide = true;
+ bool isAIGuide = true;
+
+ @override
+ void onInit() {
+ super.onInit();
+ // isHomeGuide = _storage.read('home_guide') ?? false;
+ // isAIGuide = _storage.read('ai_guide') ?? false;
+ }
+
+ void startHomeGuide() {
+ if (!isHomeGuide) {
+ isHomeGuide = true;
+ _storage.write('home_guide', true);
+ Intro.of(Get.context!).start(group: 'home');
+ }
+ }
+
+ void startAIGuide() async {
+ if (!isAIGuide) {
+ await Future.delayed(const Duration(milliseconds: 200));
+ isAIGuide = true;
+ _storage.write('ai_guide', true);
+ Intro.of(Get.context!).start(group: 'ai');
+ }
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index 7c5818e36..fd95d2863 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
+import 'package:flutter_intro/flutter_intro.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
@@ -19,6 +21,8 @@ void main() async {
// 确保Flutter绑定初始化
WidgetsFlutterBinding.ensureInitialized();
+ await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
+
// 初始化存储 - 在创建应用之前初始化
await GetStorage.init();
@@ -92,7 +96,10 @@ class MainApp extends StatelessWidget {
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.noScaling,
),
- child: widget!,
+ child: Intro(
+ padding: EdgeInsets.zero,
+ child: widget!,
+ ),
);
},
),
diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart
index dda9ed2c4..5330abd4c 100644
--- a/lib/modules/agent/controllers/agent_controller.dart
+++ b/lib/modules/agent/controllers/agent_controller.dart
@@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'package:amap_service/amap_service.dart';
+import '../../../data/services/novice_guide_service.dart';
import '/core/utils/upload_oss.dart';
import '/data/models/user_Info.dart';
import '/data/services/music_manager.dart';
@@ -39,6 +40,8 @@ class AgentController extends GetxController {
final TextEditingController textController = TextEditingController();
final ScrollController scrollController = ScrollController();
+ final noviceGuideService = Get.find();
+
//音乐播放管理
final _musiceManager = Get.find();
@@ -153,8 +156,6 @@ class AgentController extends GetxController {
_setupMediaSessionCallbacks(); //设置媒体会话回调
_setupTextInputListener(); //设置文本输入监听器
-
-
startVoiceInput(); //开启语音输入
}
@@ -1013,8 +1014,6 @@ class AgentController extends GetxController {
}
}
-
-
// 开始语音输入
Future startVoiceInput() async {
if (isListening.value) return;
@@ -1309,29 +1308,71 @@ class AgentController extends GetxController {
return;
}
- // 切换到语音模式并开始语音输入
+ // 打电话模式
isTextInputMode.value = false;
- // 打电话模式不使用按住说话模式,而是持续语音输入
- //isPushToTalkMode.value = false;
- // 开始语音识别
- Logger.i(TAG, '开始打电话模式,开始语音输入');
+ Logger.i(TAG, '打电话模式已启动,开始语音输入');
await AgentService.startConversation();
- //}
}
// 结束打电话模式
void endPhoneCall() async {
Logger.i(TAG, '🔥 结束打电话模式');
- // 退出打电话模式
- isPhoneCallMode.value = false;
+ try {
+ // 如果正在录音,先停止录音
+ if (isListening.value) {
+ final result = await AgentService.stopConversation();
- // 停止语音输入
- //await stopVoiceInput();
- // 开始语音识别
- await AgentService.stopConversation();
- // 恢复之前的输入模式,而不是强制切换到文字模式
- _restorePreviousInputMode();
+ if (result) {
+ isListening.value = false;
+ }
+ }
+
+ // 退出打电话模式
+ isPhoneCallMode.value = false;
+ _restorePreviousInputMode();
+ } catch (e) {
+ Logger.e(TAG, '结束打电话模式失败: $e');
+
+ // 即使出现异常,也要确保退出打电话模式
+ isPhoneCallMode.value = false;
+ isListening.value = false;
+ _restorePreviousInputMode();
+ }
+ }
+
+ // 打电话模式专用:切换录音状态
+ Future togglePhoneCallRecording() async {
+ Logger.i(TAG, '🎤 打电话模式切换录音状态');
+ // 只有在打电话模式下才能使用此方法
+ if (!isPhoneCallMode.value) {
+ Logger.w(TAG, '不在打电话模式,无法切换录音状态');
+ return;
+ }
+
+ try {
+ if (isListening.value) {
+ // 当前正在录音,停止录音
+ Logger.i(TAG, '停止打电话模式录音');
+ final result = await AgentService.stopConversation();
+
+ // 手动更新状态,确保UI同步
+ if (result) {
+ isListening.value = false;
+ }
+ } else {
+ // 当前未录音,开始录音
+ Logger.i(TAG, '开始打电话模式录音');
+ final result = await AgentService.startConversation();
+
+ // 手动更新状态,确保UI同步
+ if (result) {
+ isListening.value = true;
+ }
+ }
+ } catch (e) {
+ Logger.e(TAG, '打电话模式录音状态切换失败: $e');
+ }
}
// 耳机唤醒通话模式(专用入口)
diff --git a/lib/modules/agent/views/agent_view.dart b/lib/modules/agent/views/agent_view.dart
index 3703ec3f4..d989cd037 100644
--- a/lib/modules/agent/views/agent_view.dart
+++ b/lib/modules/agent/views/agent_view.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
+import 'package:flutter_intro/flutter_intro.dart';
import 'package:get/get.dart';
import '../controllers/agent_controller.dart';
import '../controllers/message_action_controller.dart';
@@ -388,96 +389,110 @@ class _AgentViewState extends State with WidgetsBindingObserver {
if (controller.isTextInputMode.value) {
// 文本输入模式 - 限制最大高度并只向上扩展
- return Container(
- constraints: BoxConstraints(
- minHeight: 44.h,
- maxHeight: 120.h,
- ),
- padding: EdgeInsets.symmetric(
- horizontal: 16.w),
- decoration: BoxDecoration(
- color: isDarkMode
- ? Colors.grey[800]
- : Colors.grey[200],
- borderRadius:
- BorderRadius.circular(22.w),
- border: Border.all(
+ return IntroStepBuilder(
+ group: 'ai',
+ order: 1,
+ text: '对小言说“今天天气怎么样”',
+ onWidgetLoad: () {
+ controller.noviceGuideService
+ .startAIGuide();
+ },
+ builder: (BuildContext context,
+ GlobalKey key) =>
+ Container(
+ key: key,
+ constraints: BoxConstraints(
+ minHeight: 44.h,
+ maxHeight: 120.h,
+ ),
+ padding: EdgeInsets.symmetric(
+ horizontal: 16.w),
+ decoration: BoxDecoration(
color: isDarkMode
- ? Colors.grey[800]!
- : Colors.grey.withOpacity(0.1),
- width: 1.w,
+ ? Colors.grey[800]
+ : Colors.grey[200],
+ borderRadius:
+ BorderRadius.circular(22.w),
+ border: Border.all(
+ color: isDarkMode
+ ? Colors.grey[800]!
+ : Colors.grey.withOpacity(0.1),
+ width: 1.w,
+ ),
),
- ),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment
- .end, // 对齐到底部,实现向上扩展
- children: [
- Expanded(
- child: Obx(() {
- // 图片模式下使用更高的输入框
- final isImageActive = controller
- .isImageInputActive.value;
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment
+ .end, // 对齐到底部,实现向上扩展
+ children: [
+ Expanded(
+ child: Obx(() {
+ // 图片模式下使用更高的输入框
+ final isImageActive = controller
+ .isImageInputActive.value;
- return TextField(
- controller:
- controller.textController,
- maxLines: isImageActive
- ? 3
- : 5, // 设置合理的最大行数
- minLines: isImageActive ? 2 : 1,
- keyboardType: TextInputType
- .multiline, // 启用多行键盘
- textInputAction: TextInputAction
- .newline, // 支持回车换行
- onTap: () {
- // 当点击输入框时,如果'+'选项已展开,则收缩
- if (controller
- .isPlusOptionsExpanded
- .value) {
- controller
- .collapsePlusOptions();
- }
- },
- style: TextStyle(
- fontSize: 15.sp,
- color: isDarkMode
- ? Colors.white
- : Colors.grey[800],
- ),
- decoration: InputDecoration(
- hintText: isImageActive
- ? 'addQuestionAboutImage'
- .tr // 添加对图片的问题或描述...
- : 'inputMessage'
- .tr, // 输入消息...
- hintStyle: TextStyle(
- color: Colors.grey[500],
+ return TextField(
+ controller:
+ controller.textController,
+ maxLines: isImageActive
+ ? 3
+ : 5, // 设置合理的最大行数
+ minLines:
+ isImageActive ? 2 : 1,
+ keyboardType: TextInputType
+ .multiline, // 启用多行键盘
+ textInputAction:
+ TextInputAction
+ .newline, // 支持回车换行
+ onTap: () {
+ // 当点击输入框时,如果'+'选项已展开,则收缩
+ if (controller
+ .isPlusOptionsExpanded
+ .value) {
+ controller
+ .collapsePlusOptions();
+ }
+ },
+ style: TextStyle(
fontSize: 15.sp,
+ color: isDarkMode
+ ? Colors.white
+ : Colors.grey[800],
),
- filled: false,
- border: InputBorder.none,
- focusedBorder:
- OutlineInputBorder(
- borderSide: BorderSide(
- color: isDarkMode
- ? Colors.grey[800]!
- : Colors.grey[200]!,
- width: 1.w,
+ decoration: InputDecoration(
+ hintText: isImageActive
+ ? 'addQuestionAboutImage'
+ .tr // 添加对图片的问题或描述...
+ : 'inputMessage'
+ .tr, // 输入消息...
+ hintStyle: TextStyle(
+ color: Colors.grey[500],
+ fontSize: 15.sp,
+ ),
+ filled: false,
+ border: InputBorder.none,
+ focusedBorder:
+ OutlineInputBorder(
+ borderSide: BorderSide(
+ color: isDarkMode
+ ? Colors.grey[800]!
+ : Colors.grey[200]!,
+ width: 1.w,
+ ),
),
+ contentPadding:
+ EdgeInsets.symmetric(
+ vertical:
+ isImageActive
+ ? 12.h
+ : 8.h),
),
- contentPadding:
- EdgeInsets.symmetric(
- vertical:
- isImageActive
- ? 12.h
- : 8.h),
- ),
- onSubmitted: (_) =>
- controller.sendMessage(),
- );
- }),
- ),
- ],
+ onSubmitted: (_) =>
+ controller.sendMessage(),
+ );
+ }),
+ ),
+ ],
+ ),
),
);
} else {
@@ -919,212 +934,60 @@ class _AgentViewState extends State with WidgetsBindingObserver {
Widget _buildPhoneCallOverlay(BuildContext context, bool isDarkMode) {
return Container(
color: Colors.transparent, // 设置整个覆盖层为透明
- child: SafeArea(
- child: Column(
- children: [
- // 顶部标题栏 - 保持背景
- // Container(
- // color: isDarkMode
- // ? Colors.black.withOpacity(0.8)
- // : Colors.white.withOpacity(0.8),
- // child: _buildPhoneCallHeader(isDarkMode),
- // ),
-
- // 中间音频可视化区域 - 完全透明
- const Expanded(
- child: SizedBox(), // 空的占位符,完全透明
- ),
-
- // 底部控制按钮 - 保持背景
- Container(
- color: isDarkMode ? Colors.black : Colors.white,
- child: _buildPhoneCallControls(isDarkMode),
- ),
- ],
- ),
- ),
- );
- }
-
- // 构建打电话模式标题栏
- Widget _buildPhoneCallHeader(bool isDarkMode) {
- return Container(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
- child: Row(
+ child: Column(
children: [
- // 返回按钮
- GestureDetector(
- onTap: () => controller.endPhoneCall(),
- child: Container(
- width: 40,
- height: 40,
- decoration: BoxDecoration(
- color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
- borderRadius: BorderRadius.circular(20),
- ),
- child: Icon(
- Icons.arrow_back,
- color: isDarkMode ? Colors.white : Colors.black,
- size: 20,
- ),
- ),
+ // 中间空白区域 - 完全透明
+ const Expanded(
+ child: SizedBox(), // 空的占位符,完全透明
),
- const SizedBox(width: 16),
-
- // 标题
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- 'aiVoiceCall'.tr, // AI语音通话
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: isDarkMode ? Colors.white : Colors.black,
- ),
- ),
- Obx(() {
- final status = controller.isListening.value
- ? 'inCall'.tr // 正在通话中...
- : controller.isSpeaking.value
- ? 'aiReplying'.tr // AI正在回复...
- : 'waitingToStart'.tr; // 等待开始...
- return Text(
- status,
- style: TextStyle(
- fontSize: 14,
- color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
- ),
- );
- }),
- ],
- ),
+ // 底部控制按钮 - 包含音频波纹
+ Container(
+ color: isDarkMode ? Colors.black : Colors.white,
+ child: _buildPhoneCallControls(isDarkMode),
),
],
),
);
}
- // 构建打电话模式音频可视化
- Widget _buildPhoneCallVisualization(bool isDarkMode) {
- return Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- // 音频可视化圆圈
- Obx(() {
- final isActive =
- controller.isListening.value || controller.isSpeaking.value;
- return AnimatedContainer(
- duration: const Duration(milliseconds: 300),
- width: isActive ? 200 : 160,
- height: isActive ? 200 : 160,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: isDarkMode
- ? Colors.blue[900]?.withOpacity(0.3)
- : Colors.blue[100],
- border: Border.all(
- color: Colors.blue,
- width: isActive ? 3 : 2,
- ),
- ),
- child: Icon(
- Icons.phone,
- size: isActive ? 80 : 60,
- color: Colors.blue,
- ),
- );
- }),
-
- const SizedBox(height: 32),
-
- // 状态文字
- Obx(() {
- String statusText = 'clickToStartCall'.tr; // 点击开始通话
- if (controller.isListening.value) {
- statusText = 'listeningToYou'.tr; // 正在听您说话...
- } else if (controller.isSpeaking.value) {
- statusText = 'aiReplying'.tr; // AI正在回复...
- } else if (controller.isProcessing.value) {
- statusText = 'processing'.tr; // 正在处理...
- }
-
- return Text(
- statusText,
- style: TextStyle(
- fontSize: 16,
- color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
- fontWeight: FontWeight.w500,
- ),
- );
- }),
- ],
- ),
- );
- }
-
// 构建打电话模式控制按钮
Widget _buildPhoneCallControls(bool isDarkMode) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 24.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.center, // 确保垂直居中对齐
children: [
- // 静音按钮
+ // 录音控制按钮(左侧) - 控制录音输入功能
Obx(() => _buildPhoneControlButton(
- icon: controller.isTtsEnabled.value
- ? Icons.volume_up
- : Icons.volume_off,
- onTap: () => controller.toggleTts(),
+ icon: controller.isListening.value
+ ? Icons.mic // 录音中显示麦克风图标
+ : Icons.mic_off, // 未录音显示麦克风关闭图标
+ onTap: () => controller.togglePhoneCallRecording(),
isDarkMode: isDarkMode,
- isActive: controller.isTtsEnabled.value,
+ isActive: controller.isListening.value,
+ // backgroundColor: controller.isListening.value
+ // ? Colors.red // 录音中显示红色背景
+ // : Theme.of(context).primaryColor, // 未录音显示主题色
)),
- // 主要通话按钮
- Obx(() {
- final isActive = controller.isListening.value;
- return GestureDetector(
- // onTap: () {
- // if (isActive) {
- // controller.stopVoiceInput();
- // } else {
- // controller.startVoiceInput();
- // }
- // },
- child: Container(
- width: 80.w,
- height: 80.h,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: isActive ? Colors.red : Colors.green,
- boxShadow: [
- BoxShadow(
- color: (isActive ? Colors.red : Colors.green)
- .withOpacity(0.3),
- blurRadius: 12.w,
- spreadRadius: 2.w,
- ),
- ],
- ),
- child: Icon(
- isActive ? Icons.call_end : Icons.call,
- color: Colors.white,
- size: 36.sp,
- ),
- ),
- );
- }),
+ // 音频波纹组件(中间) - 仅显示动态波形效果,无其他UI元素
+ Container(
+ width: 120.w, // 设置固定宽度
+ height: 64.h, // 与按钮高度一致
+ alignment: Alignment.center, // 确保波纹居中
+ child: const AgentSoundBarsWidget(barCount: 12), // 适应宽度的波纹条数
+ ),
- // 结束通话按钮
+ // 关闭打电话模式按钮(右侧) - 退出打电话模式
_buildPhoneControlButton(
- icon: Icons.call_end,
+ icon: Icons.close,
onTap: () => controller.endPhoneCall(),
isDarkMode: isDarkMode,
isActive: false,
- backgroundColor: Colors.red,
+ //backgroundColor: Colors.red,
+ iconColor: Colors.redAccent,
),
],
),
@@ -1138,24 +1001,31 @@ class _AgentViewState extends State with WidgetsBindingObserver {
required bool isDarkMode,
required bool isActive,
Color? backgroundColor,
+ Color? iconColor,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
- width: 56.w,
- height: 56.h,
+ width: 64.w, // 按钮尺寸
+ height: 64.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: backgroundColor ??
- (isDarkMode ? Colors.grey[800] : Colors.grey[200]),
- border: isActive ? Border.all(color: Colors.blue, width: 2.w) : null,
+ (isDarkMode ? Colors.grey[800] : Colors.grey[250]),
+ border: Border.all(color: Colors.blue, width: 2.w),
+ boxShadow: [
+ BoxShadow(
+ color: (backgroundColor ?? Colors.grey).withValues(alpha: 0.3),
+ blurRadius: 8.w,
+ spreadRadius: 1.w,
+ offset: Offset(0, 2.h),
+ ),
+ ],
),
child: Icon(
icon,
- color: backgroundColor != null
- ? Colors.white
- : (isDarkMode ? Colors.white : Colors.black),
- size: 24.sp,
+ color: iconColor ?? (isDarkMode ? Colors.white : Colors.black),
+ size: 28.sp, // 图标尺寸
),
),
);
diff --git a/lib/modules/agent/widgets/sound_bar_widget.dart b/lib/modules/agent/widgets/sound_bar_widget.dart
index c108bcca2..9028f713b 100644
--- a/lib/modules/agent/widgets/sound_bar_widget.dart
+++ b/lib/modules/agent/widgets/sound_bar_widget.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/agent_controller.dart';
-/// 声音柱动画组件
+/// 声音柱动画组件 (已优化)
/// 用于显示语音输入时的音频波形效果
class SoundBarWidget extends StatefulWidget {
final double delay;
@@ -37,9 +37,8 @@ class _SoundBarWidgetState extends State
parent: _controller,
curve: Curves.easeInOut,
),
- )..addListener(() {
- setState(() {});
- });
+ );
+ // 移除了 .addListener(() { setState(() {}); })
_startOrStopAnimation();
}
@@ -54,6 +53,9 @@ class _SoundBarWidgetState extends State
/// 开始或停止动画
void _startOrStopAnimation() {
+ // 确保组件仍然挂载在树上
+ if (!mounted) return;
+
if (widget.isActive) {
Future.delayed(Duration(milliseconds: (widget.delay * 1000).toInt()), () {
if (mounted && widget.isActive) {
@@ -61,9 +63,10 @@ class _SoundBarWidgetState extends State
}
});
} else {
- _controller.reset();
- // 设置为小高度的静态状态
- _controller.animateTo(0.1, duration: const Duration(milliseconds: 300));
+ // 停止并平滑地过渡到初始状态
+ _controller.stop();
+ _controller.animateTo(0.0,
+ duration: const Duration(milliseconds: 200), curve: Curves.easeOut);
}
}
@@ -77,21 +80,28 @@ class _SoundBarWidgetState extends State
Widget build(BuildContext context) {
final primaryColor = Theme.of(context).primaryColor;
- return Container(
- width: 2.5,
- height: widget.isActive ? _animation.value : 3,
- margin: const EdgeInsets.symmetric(horizontal: 1),
- decoration: BoxDecoration(
- color: widget.isActive
- ? primaryColor.withOpacity(0.8)
- : Colors.grey.withOpacity(0.4),
- borderRadius: BorderRadius.circular(1.5),
- ),
+ // 使用 AnimatedBuilder 来优化性能
+ return AnimatedBuilder(
+ animation: _animation,
+ builder: (context, child) {
+ return Container(
+ width: 2.5,
+ // 当动画不活跃时,高度固定为3;活跃时使用动画值
+ height: widget.isActive ? _animation.value : 3,
+ margin: const EdgeInsets.symmetric(horizontal: 1),
+ decoration: BoxDecoration(
+ color: widget.isActive
+ ? primaryColor.withOpacity(0.8)
+ : Colors.grey.withOpacity(0.4),
+ borderRadius: BorderRadius.circular(1.5),
+ ),
+ );
+ },
);
}
}
-/// 声音柱组合组件
+/// 声音柱组合组件 (无需改动)
/// 包含多个声音柱的组合显示
class SoundBarsWidget extends StatelessWidget {
final bool isActive;
@@ -106,7 +116,7 @@ class SoundBarsWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
- mainAxisAlignment: MainAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(barCount, (index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 1),
@@ -120,7 +130,7 @@ class SoundBarsWidget extends StatelessWidget {
}
}
-/// 带控制器集成的声音柱组件
+/// 带控制器集成的声音柱组件 (无需改动)
/// 自动从AgentController获取状态
class AgentSoundBarsWidget extends StatelessWidget {
final int barCount;
diff --git a/lib/modules/devices/controllers/devices_controller.dart b/lib/modules/devices/controllers/devices_controller.dart
index 1beadea7a..66ff69211 100644
--- a/lib/modules/devices/controllers/devices_controller.dart
+++ b/lib/modules/devices/controllers/devices_controller.dart
@@ -57,6 +57,7 @@ class DevicesController extends GetxController {
User.instance.removeUserDevice(device.id);
_bleManager.clearPairedDevices();
_bleManager.disconnect();
+ await Future.delayed(const Duration(seconds: 1));
Get.toNamed(Routes.pairing);
return;
} else {
diff --git a/lib/modules/home/controllers/home_controller.dart b/lib/modules/home/controllers/home_controller.dart
index 31db67e15..902ceb58d 100644
--- a/lib/modules/home/controllers/home_controller.dart
+++ b/lib/modules/home/controllers/home_controller.dart
@@ -1,13 +1,11 @@
import 'package:get/get.dart';
import 'dart:async';
+import '../../../data/services/novice_guide_service.dart';
import '../../../routes/app_routes.dart';
import '../../../core/utils/logger.dart';
import 'package:flutter/material.dart';
import '../../../data/services/bluetooth_manager.dart';
import '../../../data/services/ble_manager.dart';
-import '../../../data/services/version_update_service.dart';
-import '../../common/widgets/version_update_dialog.dart';
-import 'package:get_storage/get_storage.dart';
/// 连接状态枚举
enum ConnectionStatus {
@@ -24,7 +22,7 @@ class HomeController extends GetxController {
final _isFullyConnected = false.obs;
String? _lastConnectedDeviceName;
-
+
bool get isFullyConnected => _isFullyConnected.value;
RxBool get isFullyConnectedRx => _isFullyConnected;
BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice;
@@ -53,14 +51,9 @@ class HomeController extends GetxController {
final _bluetoothManager = Get.find();
final _bleManager = Get.find();
- final _storage = GetStorage();
- // 会话级别的版本检查标记(静态变量,整个应用会话期间保持)
- static bool _hasCheckedVersionInSession = false;
+ final noviceGuideService = Get.find();
- // 版本检查相关常量
- static const String _lastVersionCheckKey = 'last_version_check_timestamp';
- static const int _versionCheckIntervalHours = 24; // 24小时间隔
@override
void onInit() {
super.onInit();
@@ -78,12 +71,6 @@ class HomeController extends GetxController {
void _initWithCachedInfo() {
_setupStateListeners();
_checkConnectionStatus();
-
- // 延迟检查版本更新,避免影响页面加载
- // 混合检查机制:会话检查 + 24小时间隔检查
- Future.delayed(const Duration(seconds: 2), () {
- _checkVersionUpdateIfNeeded();
- });
}
void _setupStateListeners() {
@@ -94,13 +81,14 @@ class HomeController extends GetxController {
update();
});
- _classicBluetoothWorker = ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) {
+ _classicBluetoothWorker =
+ ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) {
if (device != null) {
_lastConnectedDeviceName = device.name;
}
_checkConnectionStatus();
});
-
+
_bleConnectionWorker = ever(_bleManager.connectionStateRx, (int state) {
_checkConnectionStatus();
});
@@ -121,30 +109,30 @@ class HomeController extends GetxController {
void exploreAIFeatures() {
Get.toNamed(Routes.agent);
}
-
+
Future _checkConnectionStatus() async {
try {
final currentBleState = await _bleManager.getConnectionState();
-
+
bool hasClassicBluetooth = currentDevice != null;
bool hasBleConnection = currentBleState == 2;
bool wasConnected = _isFullyConnected.value;
bool isNowConnected = hasClassicBluetooth && hasBleConnection;
-
+
if (isNowConnected && currentDevice == null) {
isNowConnected = false;
}
-
+
if (_isFullyConnected.value != isNowConnected) {
_isFullyConnected.value = isNowConnected;
_isFullyConnected.refresh();
update();
-
+
if (!wasConnected && isNowConnected) {
await _bleManager.fetchDeviceInfo();
}
}
-
+
update();
} catch (e) {
Logger.e(_tag, '${'checkConnectionStatusFailed'.tr}: $e');
@@ -165,26 +153,28 @@ class HomeController extends GetxController {
}
bool get shouldShowDeviceInfo => currentDevice != null;
-
+
String getDeviceName() {
if (_isFullyConnected.value && currentDevice == null) {
_checkConnectionStatus();
}
-
+
final deviceName = currentDevice?.name;
if (deviceName != null && deviceName.isNotEmpty) {
return deviceName;
}
-
+
final bleDeviceName = deviceInfo.value?.name;
if (bleDeviceName != null && bleDeviceName.isNotEmpty) {
return bleDeviceName;
}
-
- if (!_isFullyConnected.value && _lastConnectedDeviceName != null && _lastConnectedDeviceName!.isNotEmpty) {
+
+ if (!_isFullyConnected.value &&
+ _lastConnectedDeviceName != null &&
+ _lastConnectedDeviceName!.isNotEmpty) {
return _lastConnectedDeviceName!;
}
-
+
return 'unknownEquipment'.tr;
}
@@ -270,104 +260,4 @@ class HomeController extends GetxController {
? deviceInfo.value!.firmwareVersion
: 'unknown'.tr;
}
-
- /// 混合检查机制:判断是否需要进行版本检查
- /// 检查条件:1. 会话内未检查过 OR 2. 距离上次检查超过24小时
- void _checkVersionUpdateIfNeeded() {
- try {
- final now = DateTime.now();
- final lastCheckTimestamp = _storage.read(_lastVersionCheckKey);
-
- bool shouldCheck = false;
- String reason = '';
-
- // 条件1:会话内未检查过
- if (!_hasCheckedVersionInSession) {
- shouldCheck = true;
- reason = '会话内首次检查';
- }
- // 条件2:距离上次检查超过24小时
- else if (lastCheckTimestamp != null) {
- final lastCheckTime = DateTime.fromMillisecondsSinceEpoch(lastCheckTimestamp);
- final timeDifference = now.difference(lastCheckTime);
-
- if (timeDifference.inHours >= _versionCheckIntervalHours) {
- shouldCheck = true;
- reason = '距离上次检查已超过${_versionCheckIntervalHours}小时';
- }
- }
- // 条件3:从未检查过(首次使用应用)
- else {
- shouldCheck = true;
- reason = '首次使用应用';
- }
-
- if (shouldCheck) {
- Logger.i(_tag, '开始版本检查 - 原因: $reason');
- _performVersionCheck();
- } else {
- Logger.i(_tag, '跳过版本检查 - 会话内已检查且未超过时间间隔');
- }
- } catch (e) {
- Logger.e(_tag, '版本检查判断失败: $e');
- }
- }
-
- /// 执行版本检查(在后台进行,不阻塞启动流程)
- Future _performVersionCheck() async {
- try {
- final versionService = VersionUpdateService.to;
- final updateInfo = await versionService.checkForUpdate();
-
- if (updateInfo.hasUpdate) {
- Logger.i(_tag, '发现新版本: ${updateInfo.latestVersion}');
- // 显示更新对话框
- await VersionUpdateDialog.show(updateInfo);
- } else {
- Logger.i(_tag, '当前已是最新版本: ${updateInfo.currentVersion}');
- }
-
- // 更新检查记录
- _updateVersionCheckRecord();
- } catch (e) {
- Logger.e(_tag, '版本检查失败: $e');
- // 即使检查失败,也更新记录,避免频繁重试
- _updateVersionCheckRecord();
- }
- }
-
- /// 更新版本检查记录
- void _updateVersionCheckRecord() {
- try {
- // 更新时间戳
- final now = DateTime.now();
- _storage.write(_lastVersionCheckKey, now.millisecondsSinceEpoch);
-
- // 标记本次会话已检查过版本
- _hasCheckedVersionInSession = true;
-
- Logger.i(_tag, '版本检查记录已更新: ${now.toIso8601String()}');
- } catch (e) {
- Logger.e(_tag, '更新版本检查记录失败: $e');
- }
- }
-
- /// 重置版本检查标记(可用于强制重新检查)
- /// 注意:这会清除会话标记,但不会清除时间戳记录
- static void resetVersionCheckFlag() {
- _hasCheckedVersionInSession = false;
- Logger.i('HomeCtrl', '版本检查会话标记已重置');
- }
-
- /// 完全重置版本检查状态(包括时间戳)
- /// 用于测试或特殊情况下强制重新检查
- void forceResetVersionCheck() {
- try {
- _hasCheckedVersionInSession = false;
- _storage.remove(_lastVersionCheckKey);
- Logger.i(_tag, '版本检查状态已完全重置');
- } catch (e) {
- Logger.e(_tag, '重置版本检查状态失败: $e');
- }
- }
}
diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart
index 8bcbac345..f842b6a23 100644
--- a/lib/modules/home/views/home_view.dart
+++ b/lib/modules/home/views/home_view.dart
@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
+import 'package:flutter_intro/flutter_intro.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../controllers/home_controller.dart';
-import '../../../routes/app_pages.dart';
import '../../../routes/app_routes.dart';
import '../../../core/widgets/common_bottom_nav.dart';
@@ -84,11 +84,30 @@ class HomeView extends GetView {
_buildAppBar(isDarkMode),
// 顶部图片
- _buildEarbudsImage(),
+ IntroStepBuilder(
+ group: 'home',
+ order: 1,
+ text: '欢迎使用Voitrans!首先,让我们唤醒你的专属 AI—— 小言',
+ onWidgetLoad: () {
+ controller.noviceGuideService.startHomeGuide();
+ },
+ builder: (BuildContext context, GlobalKey key) => SizedBox(
+ key: key,
+ child: _buildEarbudsImage(),
+ ),
+ ),
SizedBox(height: 20.h),
// 设备状态卡片
- _buildDeviceStatusCard(isDarkMode),
+ IntroStepBuilder(
+ group: 'home',
+ order: 2,
+ text: '对着麦克风说“小言小言”',
+ builder: (BuildContext context, GlobalKey key) => SizedBox(
+ key: key,
+ child: _buildDeviceStatusCard(isDarkMode),
+ ),
+ ),
SizedBox(height: 20.h),
// 热门AI体验
@@ -623,23 +642,33 @@ class HomeView extends GetView {
// 左侧大卡片 - AI助理
Expanded(
flex: 1,
- child: _buildFeatureCard(
- title: 'aiAssistant'.tr, // AI全能助手
- description: 'aiAssistantInfo'.tr, // 24小时专属顾问,解答问题、提供建议
- icon: Icons.assistant,
- gradient: LinearGradient(
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- colors: [
- Color(0xFF0066CC).withOpacity(0.3),
- Color(0xFF5E5CE6).withOpacity(0.3),
- ],
+ child: IntroStepBuilder(
+ group: 'home',
+ order: 3,
+ text: '太棒了!你已成功唤醒小言,接下来试试让它帮你做件事吧',
+ onHighlightWidgetTap: () {
+ Intro.of(Get.context!).dispose();
+ controller.exploreAIFeatures();
+ },
+ builder: (BuildContext context, GlobalKey key) => _buildFeatureCard(
+ key: key,
+ title: 'aiAssistant'.tr, // AI全能助手
+ description: 'aiAssistantInfo'.tr, // 24小时专属顾问,解答问题、提供建议
+ icon: Icons.assistant,
+ gradient: LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [
+ Color(0xFF0066CC).withOpacity(0.3),
+ Color(0xFF5E5CE6).withOpacity(0.3),
+ ],
+ ),
+ onTap: controller.exploreAIFeatures,
+ iconOnTop: true,
+ height: 175.h, // 增加高度
+ isDarkMode: isDarkMode,
+ iconColor: const Color(0xFF0066CC),
),
- onTap: controller.exploreAIFeatures,
- iconOnTop: true,
- height: 175.h, // 增加高度
- isDarkMode: isDarkMode,
- iconColor: const Color(0xFF0066CC),
),
),
@@ -702,6 +731,7 @@ class HomeView extends GetView {
// 特性卡片构建方法
Widget _buildFeatureCard({
+ GlobalKey? key,
required String title,
required String description,
required IconData icon,
@@ -713,6 +743,7 @@ class HomeView extends GetView {
Color iconColor = Colors.white,
}) {
return GestureDetector(
+ key: key,
onTap: onTap,
child: Container(
height: height ?? 175.h,
diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart
index d267211f3..f2783f4bb 100644
--- a/lib/modules/pairing/controllers/pairing_controller.dart
+++ b/lib/modules/pairing/controllers/pairing_controller.dart
@@ -1,12 +1,10 @@
-import 'dart:convert';
-import 'dart:typed_data';
-
import 'package:app_settings/app_settings.dart';
import 'package:voitrans/data/models/appconfig_model.dart';
import '/data/models/appconfig.dart';
import '/data/models/user_Info.dart';
import '/data/services/network/api.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'dart:async';
import 'package:permission_handler/permission_handler.dart';
@@ -46,7 +44,7 @@ enum ErrorType {
}
/// 配对控制器 - 基于简化状态机的实现
-class PairingController extends GetxController {
+class PairingController extends GetxController with WidgetsBindingObserver {
static const String _tag = 'PairingCtrl';
static PairingController get to => Get.find();
@@ -102,6 +100,11 @@ class PairingController extends GetxController {
super.onInit();
Logger.i(_tag, '配对控制器初始化');
+ // 注册应用生命周期监听器
+ Logger.i(_tag, '正在注册应用生命周期监听器');
+ WidgetsBinding.instance.addObserver(this);
+ Logger.i(_tag, '应用生命周期监听器注册完成');
+
// 确保服务已初始化
_bluetoothManager = Get.find();
_bleManager = Get.find();
@@ -125,6 +128,10 @@ class PairingController extends GetxController {
@override
void onClose() {
Logger.i(_tag, '配对控制器销毁');
+
+ // 移除应用生命周期监听器
+ WidgetsBinding.instance.removeObserver(this);
+
// 清理所有Worker
for (var worker in _workers) {
worker.dispose();
@@ -137,6 +144,109 @@ class PairingController extends GetxController {
super.onClose();
}
+ /// 应用生命周期状态变化回调
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ super.didChangeAppLifecycleState(state);
+ // 使用多种日志级别确保输出
+ Logger.i(_tag, '=== 应用生命周期状态变化: $state ===');
+ print('PairingCtrl: 应用生命周期状态变化: $state'); // 直接使用print确保输出
+
+ switch (state) {
+ case AppLifecycleState.resumed:
+ // 应用从后台切回前台
+ Logger.i(_tag, '=== 应用从后台切回前台 ===');
+ print('PairingCtrl: 应用从后台切回前台');
+ _onAppResumed();
+ break;
+ case AppLifecycleState.paused:
+ // 应用切到后台
+ Logger.i(_tag, '应用切到后台');
+ print('PairingCtrl: 应用切到后台');
+ _onAppPaused();
+ break;
+ case AppLifecycleState.inactive:
+ // 应用处于非活跃状态(如来电话时)
+ Logger.i(_tag, '应用处于非活跃状态');
+ print('PairingCtrl: 应用处于非活跃状态');
+ break;
+ case AppLifecycleState.detached:
+ // 应用即将被销毁
+ Logger.i(_tag, '应用即将被销毁');
+ print('PairingCtrl: 应用即将被销毁');
+ break;
+ case AppLifecycleState.hidden:
+ // 应用被隐藏(iOS 13+)
+ Logger.i(_tag, '应用被隐藏');
+ print('PairingCtrl: 应用被隐藏');
+ break;
+ }
+ }
+
+ /// 应用从后台切回前台时的处理逻辑
+ void _onAppResumed() {
+ Logger.i(_tag, '处理应用从后台切回前台事件');
+
+ // 刷新蓝牙连接状态
+ _bluetoothManager.refreshDeviceConnection();
+
+ // 根据当前配对状态执行相应操作
+ switch (_currentState.value) {
+ case PairingState.waitingClassic:
+ // 如果在等待经典蓝牙连接,重新检查连接状态
+ Logger.i(_tag, '重新检查经典蓝牙连接状态');
+ _checkCurrentStatus();
+ break;
+ case PairingState.waitingBle:
+ // 如果在等待BLE连接,重新检查BLE状态
+ Logger.i(_tag, '重新检查BLE连接状态');
+ _checkBleConnectionAfterResume();
+ break;
+ case PairingState.error:
+ // 如果在错误状态,根据错误类型决定是否自动重试
+ if (_errorType.value == ErrorType.bluetoothOff) {
+ Logger.i(_tag, '检查蓝牙是否已重新开启');
+ _checkCurrentStatus();
+ }
+ break;
+ default:
+ // 其他状态暂不处理
+ break;
+ }
+ }
+
+ /// 应用切到后台时的处理逻辑
+ void _onAppPaused() {
+ Logger.i(_tag, '处理应用切到后台事件');
+
+ // 可以在这里暂停一些不必要的操作,如蓝牙扫描等
+ if (_currentState.value == PairingState.associating) {
+ Logger.i(_tag, '应用切到后台,暂停BLE扫描');
+ _bleManager.stopScan();
+ }
+ }
+
+ /// 应用恢复后检查BLE连接状态
+ Future _checkBleConnectionAfterResume() async {
+ try {
+ final currentBleState = await _bleManager.getConnectionState();
+ Logger.i(_tag, '应用恢复后BLE连接状态: $currentBleState');
+
+ if (currentBleState == 2) {
+ // BLE已连接,直接完成配对
+ Logger.i(_tag, '检测到BLE已连接,完成配对');
+ _bleTimeoutTimer?.cancel();
+ _transition(PairingState.completed);
+ } else {
+ // BLE未连接,重新启动连接流程
+ Logger.i(_tag, '重新启动BLE连接流程');
+ _startBleConnection();
+ }
+ } catch (e) {
+ Logger.e(_tag, '检查BLE连接状态失败: $e');
+ }
+ }
+
// ==================== 监听器设置 ====================
/// 集中设置所有监听器
@@ -202,7 +312,7 @@ class PairingController extends GetxController {
case PairingState.waitingClassic:
if (device != null) {
// 有设备连接,进入验证阶段
- Logger.i(_tag, '检测到经典蓝牙设备连接,开始验证');
+ Logger.d(_tag, '检测到经典蓝牙设备连接,开始验证');
_transition(PairingState.verifying);
_verifyDevice(device);
}
@@ -623,7 +733,7 @@ class PairingController extends GetxController {
);
product = match.isNotEmpty ? match.first : null;
}
-
+ final currentBleState = await _bleManager.getConnectionState();
final names = AppConfig.allproductNames();
// 检查是否已有绑定的设备
print("device.address${device.address}");
@@ -645,6 +755,17 @@ class PairingController extends GetxController {
// 已经绑定
return 9;
}
+ } else {
+ //未绑定设备
+
+ Logger.i(_tag, '验证时BLE状态: $currentBleState');
+ if (currentBleState == 2) {
+ //已连接 清理
+ // BleConnectionState.connected
+ Logger.i(_tag, 'BLE已连接,断开并清理缓存');
+ _bleManager.clearPairedDevices();
+ _bleManager.disconnect();
+ }
}
if (Platform.isAndroid) {
@@ -672,11 +793,15 @@ class PairingController extends GetxController {
print(
'获取到 deviceName: $deviceName address: ${device.address} names:$names');
if (names.contains(deviceName)) {
+ if (currentBleState == 2) {
+ //已连接直接返回
+ return 9;
+ }
await _bleManager.startScan(deviceName);
await Future.delayed(const Duration(seconds: 1));
// 尝试最多读取 3 次设备信息,每次间隔 1 秒
List