From 13003cd2864384b43387bafe973a5b185c1ed5d1 Mon Sep 17 00:00:00 2001 From: wolfplus Date: Mon, 10 Mar 2025 17:48:52 +0000 Subject: [PATCH] bluteeth --- .../com/example/deep_voice/MainActivity.kt | 30 +- lib/data/services/bluetooth_service.dart | 302 ++++-- .../chat/controllers/chat_controller.dart | 7 +- lib/modules/chat/views/chat_view.dart | 991 +++++++++++------- .../controllers/explore_controller.dart | 77 +- .../home/controllers/home_controller.dart | 365 +++++-- .../controllers/pairing_controller.dart | 160 +-- 7 files changed, 1274 insertions(+), 658 deletions(-) diff --git a/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt b/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt index 8843aac09..2a073e7b3 100644 --- a/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt @@ -71,7 +71,7 @@ class MainActivity: FlutterActivity() { // Azure 语音服务配置 var azureSpeechKey: String = "2K2VOHSuL8ZSEnbKgHSyTwo1ohj7C2gbxyVimsjpVUt6aETxVwlvJQQJ99BCACqBBLyXJ3w3AAAYACOGqCMj" var azureSpeechRegion: String = "southeastasia" - var volcanoAiApiKey: String = "0fd7c93b-b9bf-4b0b-bca6-e284491792bf" + var volcanoAiApiKey: String = "168deb3d-fd0c-4912-b9f1-aaee5c6743e6" // 标记是否已初始化 var isAzureHelpersInitialized = false @@ -158,20 +158,20 @@ class MainActivity: FlutterActivity() { events?.success(mapOf("type" to "channelReady")) Log.d(TAG, "已发送 channelReady 事件") - // 测试发送一个聊天记录事件 - Handler(Looper.getMainLooper()).postDelayed({ - if (voiceInteractionEventSink != null) { - Log.d(TAG, "发送测试聊天记录事件") - sendChatHistoryEvent( - "personal_assistant", - "这是一条测试用户消息", - "这是一条测试助手回复", - System.currentTimeMillis() - ) - } else { - Log.e(TAG, "无法发送测试事件:事件通道未准备好") - } - }, 5000) // 5秒后发送测试事件 + // // 测试发送一个聊天记录事件 + // Handler(Looper.getMainLooper()).postDelayed({ + // if (voiceInteractionEventSink != null) { + // Log.d(TAG, "发送测试聊天记录事件") + // sendChatHistoryEvent( + // "personal_assistant", + // "这是一条测试用户消息", + // "这是一条测试助手回复", + // System.currentTimeMillis() + // ) + // } else { + // Log.e(TAG, "无法发送测试事件:事件通道未准备好") + // } + // }, 5000) // 5秒后发送测试事件 } catch (e: Exception) { Log.e(TAG, "发送 channelReady 事件失败: ${e.message}", e) } diff --git a/lib/data/services/bluetooth_service.dart b/lib/data/services/bluetooth_service.dart index 6bed30f5d..594154acc 100644 --- a/lib/data/services/bluetooth_service.dart +++ b/lib/data/services/bluetooth_service.dart @@ -34,6 +34,9 @@ class BluetoothService extends GetxService { // 连接状态 final _connectionState = Rx(BluetoothConnectionState.disconnected); + // 存储最后连接的设备信息,供其他控制器使用 + final _lastConnectedDeviceInfo = Rx>({}); + // 订阅 StreamSubscription? _stateSubscription; StreamSubscription? _scanSubscription; @@ -51,6 +54,7 @@ class BluetoothService extends GetxService { List get scanResults => _scanResults; BluetoothDevice? get selectedDevice => _selectedDevice.value; BluetoothConnectionState get connectionState => _connectionState.value; + Map get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value; // Observable getters (for reactive programming) RxBool get isAvailableObs => _isAvailable; @@ -59,6 +63,7 @@ class BluetoothService extends GetxService { RxList get scanResultsObs => _scanResults; Rx get selectedDeviceObs => _selectedDevice; Rx get connectionStateObs => _connectionState; + Rx> get lastConnectedDeviceInfoObs => _lastConnectedDeviceInfo; /// 初始化蓝牙服务 Future init() async { @@ -66,29 +71,50 @@ class BluetoothService extends GetxService { // 不在初始化时检查权限,改为在需要时才检查 // await _checkAndRequestPermissions(); - // 监听蓝牙状态 - _stateSubscription = FlutterBluePlus.adapterState.listen((state) { - _isOn.value = state == BluetoothAdapterState.on; - _isAvailable.value = state != BluetoothAdapterState.unavailable; - - if (state == BluetoothAdapterState.on) { - // 蓝牙打开时,获取已连接设备,但不请求权限 - // 只有在用户主动扫描时才请求权限 - _getConnectedDevices(); - } else if (state == BluetoothAdapterState.off) { - // 蓝牙关闭时,清空设备列表 - _connectedDevices.clear(); - _scanResults.clear(); - _selectedDevice.value = null; - } + // 在后台线程初始化蓝牙状态监听,避免主线程阻塞 + Future.microtask(() { + // 监听蓝牙状态 + _stateSubscription = FlutterBluePlus.adapterState.listen((state) { + _isOn.value = state == BluetoothAdapterState.on; + _isAvailable.value = state != BluetoothAdapterState.unavailable; + + if (state == BluetoothAdapterState.on) { + // 蓝牙打开时,获取已连接设备,但不请求权限 + // 只有在用户主动扫描时才请求权限 + // 在后台线程执行,避免主线程阻塞 + Future.microtask(() => _getConnectedDevices()); + } else if (state == BluetoothAdapterState.off) { + // 蓝牙关闭时,清空设备列表 + _connectedDevices.clear(); + _scanResults.clear(); + _selectedDevice.value = null; + _lastConnectedDeviceInfo.value = {}; + } + }); }); - // 检查当前蓝牙状态,如果已开启则获取设备 - if (await FlutterBluePlus.isOn) { - _isOn.value = true; - _isAvailable.value = true; - _getConnectedDevices(); - } + // 不在初始化时立即检查蓝牙状态,而是延迟到UI渲染完成后 + // 这样可以避免启动时卡顿 + Future.delayed(const Duration(milliseconds: 800), () async { + try { + // 使用超时机制,避免无限等待蓝牙 + bool isOn = await FlutterBluePlus.isOn + .timeout(const Duration(seconds: 3), onTimeout: () { + Logger.error('检查蓝牙状态超时'); + return false; + }); + + _isOn.value = isOn; + _isAvailable.value = isOn; + + if (isOn) { + // 在后台线程获取设备,避免主线程阻塞 + Future.microtask(() => _getConnectedDevices()); + } + } catch (e) { + Logger.error('延迟检查蓝牙状态失败: $e'); + } + }); return this; } catch (e) { @@ -114,14 +140,21 @@ class BluetoothService extends GetxService { /// 获取已连接的设备 Future _getConnectedDevices() async { try { - // 获取已连接的设备 - List devices = await FlutterBluePlus.connectedSystemDevices; + // 使用超时机制,避免无限等待 + List devices = await FlutterBluePlus.connectedSystemDevices + .timeout(const Duration(seconds: 3), onTimeout: () { + Logger.error('获取已连接设备超时'); + return []; + }); + _connectedDevices.value = devices; // 如果有已连接的设备但没有选中设备,则选中第一个 if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) { _selectedDevice.value = _connectedDevices[0]; _monitorConnectionState(_selectedDevice.value!); + // 更新最后连接的设备信息 + _updateLastConnectedDeviceInfo(); } Logger.info('已连接设备数量: ${_connectedDevices.length}'); @@ -142,15 +175,17 @@ class BluetoothService extends GetxService { } } - /// 开始扫描附近的蓝牙设备 - Future startScan({Duration? timeout}) async { - if (_isScanning.value) return; + /// 开始扫描附近的蓝牙设备并尝试连接最强信号设备 + Future scanAndConnectToDevice({Duration? timeout}) async { + if (_isScanning.value) { + await stopScan(); + } // 清空旧的扫描结果 _scanResults.clear(); try { - // 开始扫描 + // 设置扫描监听 _scanSubscription?.cancel(); _scanSubscription = FlutterBluePlus.scanResults.listen((results) { _scanResults.value = results; @@ -161,16 +196,34 @@ class BluetoothService extends GetxService { // 开始扫描 await FlutterBluePlus.startScan( - timeout: timeout ?? const Duration(seconds: 10), + timeout: timeout ?? const Duration(seconds: 5), ); - // 扫描完成后更新状态 + // 扫描完成 _isScanning.value = false; - Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备'); + + // 如果找到设备,尝试连接信号最强的 + if (_scanResults.isNotEmpty) { + // 按信号强度排序 + List sortedResults = List.from(_scanResults); + sortedResults.sort((a, b) => b.rssi.compareTo(a.rssi)); + + // 尝试连接信号最强的设备 + final topDevice = sortedResults.first.device; + Logger.info('尝试连接信号最强的设备: ${topDevice.platformName}'); + bool success = await connectToDevice(topDevice); + + if (success) { + return topDevice; + } + } + + return null; } catch (e) { _isScanning.value = false; - Logger.error('蓝牙扫描失败: $e'); + Logger.error('蓝牙扫描或连接失败: $e'); + return null; } } @@ -186,66 +239,56 @@ class BluetoothService extends GetxService { } } - /// 连接到指定设备 + /// 连接到指定的蓝牙设备 - 简化版本 Future connectToDevice(BluetoothDevice device) async { - if (_selectedDevice.value == device) { - // 如果是当前选中设备,检查连接状态 - if (_connectionState.value == BluetoothConnectionState.connected) { - Logger.info('设备 ${device.platformName} 已经连接,无需重新连接'); - return true; - } - } + Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})'); try { - // 检查设备是否已连接 - bool isAlreadyConnected = false; - // 检查设备连接状态 - BluetoothConnectionState state = await device.connectionState.first; - if (state == BluetoothConnectionState.connected) { - Logger.info('设备 ${device.platformName} 已处于连接状态'); - isAlreadyConnected = true; - } - - // 如果设备已连接,直接更新状态而不尝试连接 - if (isAlreadyConnected) { - // 断开当前跟踪的设备(如果有) - if (_selectedDevice.value != null && _selectedDevice.value != device) { - _connectionSubscription?.cancel(); - } - - // 更新状态 - _selectedDevice.value = device; - _monitorConnectionState(device); - + // 如果是当前已连接设备,直接返回成功 + if (_selectedDevice.value == device && + _connectionState.value == BluetoothConnectionState.connected) { + Logger.info('设备已连接,无需重新连接'); return true; } // 断开当前连接 - await disconnectFromDevice(); + if (_selectedDevice.value != null && _selectedDevice.value != device) { + await disconnectFromDevice(); + } - // 连接新设备 + // 直接尝试连接 + Logger.info('直接连接设备 ${device.platformName}'); await device.connect(); - // 更新状态 + // 更新设备信息 _selectedDevice.value = device; _monitorConnectionState(device); + _updateLastConnectedDeviceInfo(); + Logger.info('设备 ${device.platformName} 连接成功'); return true; } catch (e) { - Logger.error('连接蓝牙设备失败: $e'); + Logger.error('连接设备 ${device.platformName} 失败: $e'); return false; } } /// 断开与当前设备的连接 Future disconnectFromDevice() async { - if (_selectedDevice.value == null) return true; + if (_selectedDevice.value == null) { + Logger.info('没有当前选中的设备,无需断开连接'); + return true; + } try { + Logger.info('正在断开设备 ${_selectedDevice.value!.platformName} 的连接'); await _selectedDevice.value!.disconnect(); + Logger.info('设备连接已断开,取消连接状态监听'); _connectionSubscription?.cancel(); _connectionSubscription = null; _selectedDevice.value = null; + // 清除最后连接的设备信息 + _lastConnectedDeviceInfo.value = {}; return true; } catch (e) { @@ -256,14 +299,35 @@ class BluetoothService extends GetxService { /// 监控连接状态 void _monitorConnectionState(BluetoothDevice device) { - _connectionSubscription?.cancel(); + Logger.info('设置设备 ${device.platformName} 的连接状态监听'); + // 取消之前的监听 + if (_connectionSubscription != null) { + Logger.info('取消之前的连接状态监听'); + _connectionSubscription!.cancel(); + _connectionSubscription = null; + } + + // 设置新的监听 _connectionSubscription = device.connectionState.listen((state) { + Logger.info('设备 ${device.platformName} 连接状态变更为: $state'); _connectionState.value = state; if (state == BluetoothConnectionState.disconnected) { + Logger.info('设备 ${device.platformName} 已断开连接'); _selectedDevice.value = null; + // 设备断开连接时,不清除最后连接的设备信息,以便下次可以重新连接 + } else if (state == BluetoothConnectionState.connected) { + Logger.info('设备 ${device.platformName} 已成功连接'); + // 设备连接成功时,更新最后连接的设备信息 + _updateLastConnectedDeviceInfo(); + } else if (state == BluetoothConnectionState.connecting) { + Logger.info('设备 ${device.platformName} 正在连接中...'); + } else if (state == BluetoothConnectionState.disconnecting) { + Logger.info('设备 ${device.platformName} 正在断开连接...'); } }); + + Logger.info('设备 ${device.platformName} 的连接状态监听已设置'); } /// 获取已连接设备的信息 @@ -275,7 +339,7 @@ class BluetoothService extends GetxService { }; } - return { + final deviceInfo = { 'connected': _connectionState.value == BluetoothConnectionState.connected, 'device': { 'name': _selectedDevice.value!.platformName.isEmpty ? @@ -287,6 +351,112 @@ class BluetoothService extends GetxService { ?.rssi } }; + + // 更新最后连接的设备信息 + _lastConnectedDeviceInfo.value = deviceInfo; + + return deviceInfo; + } + + /// 更新最后连接的设备信息 + void _updateLastConnectedDeviceInfo() { + if (_selectedDevice.value != null) { + _lastConnectedDeviceInfo.value = { + 'connected': _connectionState.value == BluetoothConnectionState.connected, + 'device': { + 'name': _selectedDevice.value!.platformName.isEmpty ? + '未命名设备' : _selectedDevice.value!.platformName, + 'id': _selectedDevice.value!.remoteId.str, + 'isConnected': _connectionState.value == BluetoothConnectionState.connected, + 'rssi': _scanResults + .firstWhereOrNull((r) => r.device.remoteId == _selectedDevice.value!.remoteId) + ?.rssi + } + }; + } + } + + /// 一个综合方法,执行自动查找并连接设备的完整流程 + Future autoConnectDevice() async { + try { + // 1. 先检查当前已连接设备 + if (_selectedDevice.value != null && + _connectionState.value == BluetoothConnectionState.connected) { + Logger.info('使用当前已连接设备: ${_selectedDevice.value!.platformName}'); + return _selectedDevice.value; + } + + // 2. 检查系统中已连接的设备 + try { + List connectedDevices = await FlutterBluePlus.connectedSystemDevices + .timeout(const Duration(seconds: 3)); + + if (connectedDevices.isNotEmpty) { + // 有已连接的设备,直接使用第一个 + Logger.info('发现系统已连接设备: ${connectedDevices.first.platformName}'); + + // 连接设备并更新服务状态 + await connectToDevice(connectedDevices.first); + return connectedDevices.first; + } + } catch (e) { + Logger.error('检查系统连接设备失败: $e'); + } + + // 3. 尝试连接之前用过的设备(从已配对设备中寻找) + List pairedDevices = await FlutterBluePlus.bondedDevices; + + // 如果有配对设备,尝试连接第一个 + if (pairedDevices.isNotEmpty) { + Logger.info('尝试连接已配对设备: ${pairedDevices.first.platformName}'); + bool success = await connectToDevice(pairedDevices.first); + if (success) { + return pairedDevices.first; + } + } + + // 4. 没有找到可用设备 + return null; + } catch (e) { + Logger.error('自动连接设备流程失败: $e'); + return null; + } + } + + /// 获取设备连接状态的简化方法 (用于兼容现有代码) + Future getReliableConnectionState(BluetoothDevice device) async { + try { + // 直接获取设备当前状态 + BluetoothConnectionState state = await device.connectionState.first + .timeout(const Duration(seconds: 2), onTimeout: () { + Logger.info('获取设备 ${device.platformName} 连接状态超时'); + return BluetoothConnectionState.disconnected; + }); + + // 如果获取到的状态是已连接,直接返回 + if (state == BluetoothConnectionState.connected) { + return BluetoothConnectionState.connected; + } + + // 作为备用检查,看设备是否在系统已连接列表中 + try { + List connectedDevices = await FlutterBluePlus.connectedSystemDevices + .timeout(const Duration(seconds: 2)); + + for (var connectedDevice in connectedDevices) { + if (connectedDevice.remoteId.str == device.remoteId.str) { + return BluetoothConnectionState.connected; + } + } + } catch (e) { + // 忽略检查系统设备列表的错误 + } + + return state; + } catch (e) { + Logger.error('获取设备 ${device.platformName} 连接状态失败: $e'); + return BluetoothConnectionState.disconnected; + } } @override diff --git a/lib/modules/chat/controllers/chat_controller.dart b/lib/modules/chat/controllers/chat_controller.dart index abbfb3bb0..138c00468 100644 --- a/lib/modules/chat/controllers/chat_controller.dart +++ b/lib/modules/chat/controllers/chat_controller.dart @@ -257,6 +257,8 @@ class ChatController extends GetxController { Future sendMessage(String text) async { if (text.trim().isEmpty) return; + _voiceInteractionService.pauseVoiceInteraction(); + // 中断当前响应 _interruptCurrentResponse("发送新消息,中断当前响应"); @@ -419,7 +421,8 @@ class ChatController extends GetxController { // 初始化语音识别 await _asrService.initialize(language: 'zh-CN'); - // 启动连续语音识别 + // 启动连续语音识别 + // 注意:在UI更新后,使用模拟波形动画替代实际麦克风输入的波形数据 final recognitionStream = await _asrService.startContinuousRecognition(); isVoiceRecognitionActive.value = true; @@ -429,6 +432,8 @@ class ChatController extends GetxController { // 重置临时消息索引 _tempMessageIndex = -1; + + // 监听识别事件 _recognitionSubscription = recognitionStream.listen( _handleRecognitionEvent, diff --git a/lib/modules/chat/views/chat_view.dart b/lib/modules/chat/views/chat_view.dart index 0afae9ed0..94cd42aba 100644 --- a/lib/modules/chat/views/chat_view.dart +++ b/lib/modules/chat/views/chat_view.dart @@ -8,160 +8,140 @@ import '../models/message_model.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/widgets/message_bubble.dart'; -class ChatView extends StatefulWidget { +class ChatView extends GetView { const ChatView({Key? key}) : super(key: key); - @override - State createState() => _ChatViewState(); -} - -class _ChatViewState extends State with TickerProviderStateMixin { - // 获取控制器 - ChatController get controller => Get.find(); - - // 添加ScrollController用于控制滚动 - final ScrollController _scrollController = ScrollController(); - - // 添加一个动画控制器,用于波形动画 - late final AnimationController _waveAnimationController; - - // 添加录音控制器,用于获取麦克风数据 - late final RecorderController _recorderController; - - @override - void initState() { - super.initState(); - - // 初始化动画控制器 - _waveAnimationController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 1000), - )..repeat(); - - // 初始化录音控制器 - _recorderController = RecorderController() - ..androidEncoder = AndroidEncoder.aac - ..androidOutputFormat = AndroidOutputFormat.mpeg4 - ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC - ..sampleRate = 44100 - ..bitRate = 128000 - ..updateFrequency = const Duration(milliseconds: 50); // 更新频率 - - // 初始化录音控制器 - _initializeRecorder(); - - // 监听消息列表变化,自动滚动到底部 - ever(controller.messages, (_) { - // 使用Future.delayed确保在UI更新后滚动 - Future.delayed(Duration(milliseconds: 50), () { - if (_scrollController.hasClients && - _scrollController.position.maxScrollExtent > 0 && - (_scrollController.position.maxScrollExtent - _scrollController.offset) < 200) { - _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: Duration(milliseconds: 100), - curve: Curves.easeOut, - ); - } - }); - }); - - // 监听语音识别状态变化 - ever(controller.isVoiceRecognitionActive, (isActive) { - if (isActive) { - // 开始录音以获取波形数据 - _recorderController.record(); - } else { - // 停止录音 - _recorderController.stop(); - } - }); - } - - // 初始化录音控制器 - Future _initializeRecorder() async { - try { - // 请求录音权限 - await _recorderController.checkPermission(); - - // 如果当前正在进行语音识别,开始录音 - if (controller.isVoiceRecognitionActive.value) { - await _recorderController.record(); - } - } catch (e) { - print('初始化录音控制器失败: $e'); - } - } - - @override - void dispose() { - _scrollController.dispose(); - _waveAnimationController.dispose(); - _recorderController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, // 设置Scaffold背景为白色 - appBar: AppBar( - leading: IconButton( - icon: Icon(Icons.arrow_back_ios, color: Colors.black), - onPressed: () => Get.back(), - ), - title: Row( - children: [ - CircleAvatar( - backgroundImage: AssetImage(controller.agent.avatarUrl), - radius: 18.r, + backgroundColor: Colors.grey[50], // 使用浅灰背景,类似HTML模板 + appBar: _buildAppBar(), + body: _buildBody(), + ); + } + + // 构建AppBar + PreferredSizeWidget _buildAppBar() { + return AppBar( + elevation: 1, + backgroundColor: Colors.white, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black87, size: 20.r), + onPressed: () => Get.back(), + ), + titleSpacing: 0, + title: Row( + children: [ + // AI头像 + Container( + width: 40.r, + height: 40.r, + decoration: BoxDecoration( + color: AppColors.primary, + borderRadius: BorderRadius.circular(20.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 3, + offset: Offset(0, 1), + ), + ], ), - SizedBox(width: 10.w), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - controller.agent.name, - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.bold, - color: Colors.black, + child: ClipRRect( + borderRadius: BorderRadius.circular(20.r), + child: controller.agent.avatarUrl.isNotEmpty + ? Image.asset( + controller.agent.avatarUrl, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.smart_toy_rounded, + color: Colors.white, + size: 22.r, + ); + }, + ) + : Icon( + Icons.smart_toy_rounded, + color: Colors.white, + size: 22.r, ), + ), + ), + SizedBox(width: 12.w), + // AI名称和状态 + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + controller.agent.name, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.bold, + color: Colors.black87, ), - Text( - controller.agent.description.length > 20 - ? '${controller.agent.description.substring(0, 20)}...' - : controller.agent.description, - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey, + ), + Row( + children: [ + Container( + width: 6.r, + height: 6.r, + decoration: BoxDecoration( + color: Colors.green, + shape: BoxShape.circle, + ), ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], + SizedBox(width: 4.w), + Text( + '在线', + style: TextStyle( + fontSize: 12.sp, + color: Colors.green, + ), + ), + ], + ), + ], + ), + ), + ], + ), + actions: [ + // TTS开关 + Obx(() => IconButton( + icon: Icon( + controller.isTtsEnabled.value + ? Icons.volume_up + : Icons.volume_off, + color: controller.isTtsEnabled.value + ? AppColors.primary + : Colors.grey, + ), + onPressed: controller.toggleTts, + )), + // 更多选项 + Container( + width: 34.r, + height: 34.r, + margin: EdgeInsets.only(right: 8.w), + decoration: BoxDecoration( + color: Colors.grey[100], + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 2, + spreadRadius: 0.5, ), + ], + ), + child: PopupMenuButton( + icon: Icon(Icons.more_vert, color: Colors.black87, size: 20.r), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.r), ), - ], - ), - actions: [ - // TTS开关 - Obx(() => IconButton( - icon: Icon( - controller.isTtsEnabled.value - ? Icons.volume_up - : Icons.volume_off, - color: controller.isTtsEnabled.value - ? AppColors.primary - : Colors.grey, - ), - onPressed: controller.toggleTts, - )), - // 更多选项 - PopupMenuButton( - icon: Icon(Icons.more_vert, color: Colors.black), itemBuilder: (context) => [ PopupMenuItem( value: 'clear', @@ -180,316 +160,545 @@ class _ChatViewState extends State with TickerProviderStateMixin { } }, ), + ), + ], + ); + } + + // 构建主体内容 + Widget _buildBody() { + return Column( + children: [ + // 消息列表 + Expanded( + child: Container( + color: Colors.grey[50], // 使用浅灰背景 + child: Obx(() => controller.messages.isEmpty + ? _buildEmptyChat() + : _buildChatList()), + ), + ), + + // 输入区域 - 根据当前模式显示不同的输入面板 + Obx(() => controller.isVoiceRecognitionActive.value + ? _buildVoiceInputPanel() + : _buildTextInputPanel()), + ], + ); + } + + // 构建空聊天界面 + Widget _buildEmptyChat() { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 80.r, + height: 80.r, + decoration: BoxDecoration( + color: AppColors.primary.withOpacity(0.1), + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(40.r), + child: controller.agent.avatarUrl.isNotEmpty + ? Image.asset( + controller.agent.avatarUrl, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.smart_toy_rounded, + size: 40.r, + color: AppColors.primary, + ); + }, + ) + : Icon( + Icons.smart_toy_rounded, + size: 40.r, + color: AppColors.primary, + ), + ), + ), + SizedBox(height: 24.h), + Text( + '你好!我是${controller.agent.name}助手', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + SizedBox(height: 12.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 40.w), + child: Text( + '我能帮你回答问题、实时语音翻译、推荐音乐和播客等。有什么我可以帮你的吗?', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14.sp, + color: Colors.grey[600], + height: 1.5, + ), + ), + ), ], - elevation: 0, - backgroundColor: Colors.white, ), - body: Container( - color: Colors.white, // 确保body背景也是白色 - child: Column( - children: [ - // 消息列表 - Expanded( - child: Obx(() => ListView.builder( - controller: controller.scrollController, - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h), - itemCount: controller.messages.length, - reverse: false, - physics: const AlwaysScrollableScrollPhysics(), - cacheExtent: 1000, // 增加缓存范围,减少重建 - itemBuilder: (context, index) { - final message = controller.messages[index]; - return MessageBubble( - key: ObjectKey(message), // 使用ObjectKey而不是ValueKey - message: message, - isUser: message.role == 'user', - ); - }, - )), + ); + } + + // 构建聊天列表 + Widget _buildChatList() { + return ListView.builder( + controller: controller.scrollController, + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h), + itemCount: controller.messages.length, + reverse: false, + physics: const AlwaysScrollableScrollPhysics(), + cacheExtent: 1000, // 增加缓存范围,减少重建 + itemBuilder: (context, index) { + final message = controller.messages[index]; + return _buildMessageItem(message); + }, + ); + } + + // 构建消息项 + Widget _buildMessageItem(Message message) { + final bool isUser = message.role == 'user'; + + return Padding( + padding: EdgeInsets.only(bottom: 16.h), + child: Row( + mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // AI头像(非用户消息时显示) + if (!isUser) + Container( + width: 36.r, + height: 36.r, + margin: EdgeInsets.only(right: 8.w, top: 2.h), + decoration: BoxDecoration( + color: AppColors.primary, + borderRadius: BorderRadius.circular(18.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 2, + offset: Offset(0, 1), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(18.r), + child: controller.agent.avatarUrl.isNotEmpty + ? Image.asset( + controller.agent.avatarUrl, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.smart_toy_rounded, + color: Colors.white, + size: 20.r, + ); + }, + ) + : Icon( + Icons.smart_toy_rounded, + color: Colors.white, + size: 20.r, + ), + ), ), - // 输入区域 - 根据当前模式显示不同的输入面板 - Obx(() => controller.isVoiceRecognitionActive.value - ? _buildVoiceInputPanel() - : _buildTextInputPanel()), - ], - ), + // 消息气泡 + Flexible( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), + decoration: BoxDecoration( + color: isUser ? AppColors.primary : Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(isUser ? 16.r : 0), + topRight: Radius.circular(isUser ? 0 : 16.r), + bottomLeft: Radius.circular(16.r), + bottomRight: Radius.circular(16.r), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 3, + spreadRadius: 0.5, + offset: Offset(0, 1), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 消息内容 + if (message.content.isNotEmpty) + SelectableText( + message.content, + style: TextStyle( + color: isUser ? Colors.white : Colors.black87, + fontSize: 15.sp, + height: 1.5, + letterSpacing: 0.2, + ), + textAlign: TextAlign.left, + maxLines: null, + enableInteractiveSelection: true, + textWidthBasis: TextWidthBasis.longestLine, + ), + + // 加载指示器 + if (message.isLoading) + Padding( + padding: EdgeInsets.only(top: message.content.isNotEmpty ? 8.h : 0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 12.w, + height: 12.h, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + isUser ? Colors.white : AppColors.primary, + ), + ), + ), + SizedBox(width: 8.w), + Text( + '思考中...', + style: TextStyle( + color: isUser ? Colors.white : Colors.black54, + fontSize: 12.sp, + ), + ), + ], + ), + ), + ], + ), + ), + ), + + // 用户头像(用户消息时显示) + if (isUser) + Container( + width: 36.r, + height: 36.r, + margin: EdgeInsets.only(left: 8.w, top: 2.h), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(18.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 2, + offset: Offset(0, 1), + ), + ], + ), + child: Center( + child: Icon( + Icons.person, + color: Colors.grey[600], + size: 20.r, + ), + ), + ), + ], ), ); } // 构建文本输入面板 Widget _buildTextInputPanel() { - // 创建一个TextEditingController来控制输入框 - final TextEditingController textController = TextEditingController(text: controller.inputText.value); + final textController = TextEditingController(text: controller.inputText.value); - // 监听输入变化 textController.addListener(() { controller.inputText.value = textController.text; }); return Container( - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 5, + color: Colors.black.withOpacity(0.06), + blurRadius: 10, offset: Offset(0, -1), ), ], ), - child: Row( - children: [ - // 语音输入按钮 - IconButton( - icon: Icon( - Icons.mic_none, - color: Colors.grey, + child: SafeArea( + child: Row( + children: [ + // 语音输入按钮 + Container( + width: 40.r, + height: 40.r, + decoration: BoxDecoration( + color: Colors.grey[100], + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 3, + spreadRadius: 0.5, + ), + ], + ), + child: IconButton( + icon: Icon( + Icons.mic_none, + color: Colors.grey[600], + size: 20.r, + ), + padding: EdgeInsets.zero, + onPressed: controller.startVoiceRecognition, + ), ), - onPressed: controller.startVoiceRecognition, - ), - // 文本输入框 - Expanded( - child: TextField( - controller: textController, - decoration: InputDecoration( - hintText: '发消息...', - border: OutlineInputBorder( + + // 文本输入框 + Expanded( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration( + color: Colors.grey[100], borderRadius: BorderRadius.circular(20.r), - borderSide: BorderSide.none, ), - filled: true, - fillColor: Colors.grey[200], - contentPadding: EdgeInsets.symmetric( - horizontal: 16.w, - vertical: 8.h, + child: TextField( + controller: textController, + decoration: InputDecoration( + hintText: '输入消息...', + hintStyle: TextStyle(color: Colors.grey[500]), + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 16.w, + vertical: 10.h, + ), + ), + maxLines: null, + textInputAction: TextInputAction.send, + style: TextStyle( + fontSize: 15.sp, + color: Colors.black87, + ), + onSubmitted: (value) { + if (value.trim().isNotEmpty) { + controller.sendMessage(value); + textController.clear(); + } + }, ), ), - maxLines: null, - textInputAction: TextInputAction.send, - onSubmitted: (value) { - if (value.trim().isNotEmpty) { - controller.sendMessage(value); - textController.clear(); - } - }, ), - ), - // 发送按钮 - Obx(() => IconButton( + + // 发送按钮 + Obx(() => Container( + width: 40.r, + height: 40.r, + decoration: BoxDecoration( + color: controller.inputText.value.trim().isEmpty + ? Colors.grey[100] + : AppColors.primary, + shape: BoxShape.circle, + boxShadow: controller.inputText.value.trim().isEmpty + ? [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 3, + spreadRadius: 0.5, + ), + ] + : [ + BoxShadow( + color: AppColors.primary.withOpacity(0.3), + blurRadius: 5, + spreadRadius: 0.5, + ), + ], + ), + child: IconButton( icon: Icon( Icons.send, + size: 18.r, color: controller.inputText.value.trim().isEmpty - ? Colors.grey - : AppColors.primary, + ? Colors.grey[500] + : Colors.white, ), + padding: EdgeInsets.zero, onPressed: controller.inputText.value.trim().isEmpty ? null : () { controller.sendMessage(controller.inputText.value); textController.clear(); }, - )), - ], + ), + )), + ], + ), ), ); } // 构建语音输入面板 Widget _buildVoiceInputPanel() { - final screenWidth = Get.width; - return Container( - width: screenWidth, - decoration: const BoxDecoration( + decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ - Color(0xFFFFFFFF), // 纯白色开始 - Color(0xFFE8D5F5), // 更深的紫色结束 + Colors.white, + Color(0xFFE8D5F5), ], - stops: [0.3, 1.0], // 调整颜色分布,让白色占据更多空间 + stops: [0.3, 1.0], ), + // boxShadow: [ + // BoxShadow( + // color: Colors.black.withOpacity(0.06), + // blurRadius: 10, + // offset: Offset(0, -1), + // ), + // ], ), child: SafeArea( child: SizedBox( - height: 100.h, - child: Center( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // 左侧麦克风按钮 - _buildMicrophoneButton(), - - // 中间音频波形和状态文本 - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // 音频波形 - _buildAudioWaveform(), - SizedBox(height: 10.h), - // 状态文本 - Obx(() => Text( - controller.isSpeechDetected.value ? '正在聆听...' : '你可以开始说话', - style: TextStyle( - fontSize: 16.sp, - color: Colors.grey.shade700, - fontWeight: FontWeight.w500, - ), - )), - ], + height: 80.h, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 30.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // 语音录制按钮 + Container( + width: 60.r, + height: 60.r, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 8, + spreadRadius: 1, + ), + ], + ), + child: Center( + child: Obx(() => Icon( + controller.isListening.value ? Icons.mic : Icons.mic_none, + color: controller.isListening.value ? AppColors.primary : Colors.grey.shade400, + size: 28.r, + )), + ), + ), + + // 中间音频波形和状态文本 + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 音频波形区域 + Container( + height: 50.h, + child: Obx(() { + final isSpeaking = controller.isSpeechDetected.value; + return Center( + child: Container( + width: 220.w, + height: 30.h, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + 23, + (index) { + // 随机高度模拟波形 + double height = isSpeaking + ? (Random().nextDouble() * 20.0 + 5.0).h + : 2.h; + + return SizedBox( + width: 6.w, + child: Center( + child: AnimatedContainer( + duration: Duration(milliseconds: 500), + width: 2.5.w, + height: height, + margin: EdgeInsets.symmetric(horizontal: 1.5.w), + decoration: BoxDecoration( + color: isSpeaking + ? AppColors.primary + : Colors.grey.shade400, + borderRadius: BorderRadius.circular(2.r), + ), + ), + ), + ); + }, + ), + ), + ), + ); + }), + ), + SizedBox(height: 8.h), + + // 语音状态文本 + Obx(() => Text( + controller.isSpeechDetected.value ? '正在聆听...' : '你可以开始说话', + style: TextStyle( + fontSize: 14.sp, + color: Colors.grey.shade700, + fontWeight: FontWeight.w500, + ), + )), + ], + ), + ), + + // 关闭按钮 + Container( + width: 60.r, + height: 60.r, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 8, + spreadRadius: 1, + ), + ], + ), + child: Center( + child: IconButton( + icon: Icon( + Icons.close, + color: Colors.grey.shade600, + size: 28.r, + ), + padding: EdgeInsets.zero, + onPressed: controller.stopVoiceRecognition, ), ), - - // 右侧关闭按钮 - _buildCloseButton(), - ], - ), + ), + ], ), ), ), ), ); } - - // 麦克风按钮 - Widget _buildMicrophoneButton() { - return Container( - width: 60.w, - height: 60.w, - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - spreadRadius: 1, - ), - ], - ), - child: Center( - child: Obx(() => IconButton( - icon: Icon( - controller.isListening.value ? Icons.mic : Icons.mic_none, - color: controller.isListening.value ? AppColors.primary : Colors.grey.shade400, - size: 28.sp, - ), - onPressed: controller.stopVoiceRecognition, - padding: EdgeInsets.zero, - )), - ), - ); - } - - // 关闭按钮 - Widget _buildCloseButton() { - return Container( - width: 60.w, - height: 60.w, - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - spreadRadius: 1, - ), - ], - ), - child: Center( - child: IconButton( - icon: Icon( - Icons.close, - color: Colors.grey.shade600, - size: 28.sp, - ), - onPressed: controller.stopVoiceRecognition, - padding: EdgeInsets.zero, - ), - ), - ); - } - - // 音频波形 - Widget _buildAudioWaveform() { - return SizedBox( - width: 220.w, - height: 50.h, - child: Obx(() { - final isSpeaking = controller.isSpeechDetected.value; - final isRec = controller.isListening.value; - - if (!isRec) { - // 如果没有在录音,显示静态波形 - return _buildStaticWaveform(); - } - - // 使用AudioWaveforms组件显示实时波形 - return AudioWaveforms( - enableGesture: false, - size: Size(220.w, 50.h), - recorderController: _recorderController, - waveStyle: WaveStyle( - waveColor: isSpeaking - ? AppColors.primary - : AppColors.primary.withOpacity(0.3), - extendWaveform: true, - showMiddleLine: false, - spacing: 4.0, - waveThickness: 3.0, - showDurationLabel: false, - scaleFactor: 80, // 调整波形高度 - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12.r), - color: Colors.transparent, - ), - padding: EdgeInsets.symmetric(horizontal: 8.w), - margin: EdgeInsets.only(bottom: 5.h), - ); - }), - ); - } - - // 静态波形(当没有录音时显示) - Widget _buildStaticWaveform() { - final barCount = 23; // 条形数量 - - return Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: List.generate( - barCount, - (index) { - // 静态高度 - final height = 2.h; - - return SizedBox( - width: 5.5.w, - child: Center( - child: Container( - width: 2.5.w, - height: height, - margin: EdgeInsets.symmetric(horizontal: 1.2.w), - decoration: BoxDecoration( - color: Colors.grey.shade400, - borderRadius: BorderRadius.circular(2.r), - ), - ), - ), - ); - }, - ), - ); - } } \ No newline at end of file diff --git a/lib/modules/explore/controllers/explore_controller.dart b/lib/modules/explore/controllers/explore_controller.dart index bbca18d55..c9cf6d1e0 100644 --- a/lib/modules/explore/controllers/explore_controller.dart +++ b/lib/modules/explore/controllers/explore_controller.dart @@ -1,5 +1,7 @@ import 'package:get/get.dart'; import 'package:flutter/material.dart'; +import '../../../data/providers/agent_provider.dart'; +import '../../../core/routes/app_routes.dart'; class AIFeature { final String title; @@ -9,6 +11,8 @@ class AIFeature { final Color endColor; final Color iconBgColor; final Color iconColor; + final String agentId; + final String targetRoute; AIFeature({ required this.title, @@ -18,6 +22,8 @@ class AIFeature { required this.endColor, required this.iconBgColor, required this.iconColor, + required this.agentId, + required this.targetRoute, }); } @@ -46,13 +52,26 @@ class ExploreController extends GetxController { // AI功能列表 final List aiFeatures = [ AIFeature( - title: '同声翻译', - description: '实时翻译40+种语言', - icon: Icons.translate, + title: '全能助理', + description: '帮你解决各类问题', + icon: Icons.assistant, startColor: Colors.blue.shade100, endColor: Colors.blue.shade200, iconBgColor: Colors.blue.withOpacity(0.2), iconColor: Colors.blue.shade600, + agentId: 'personal_assistant', + targetRoute: Routes.chat, + ), + AIFeature( + title: '同声翻译', + description: '实时翻译40+种语言', + icon: Icons.translate, + startColor: Colors.green.shade100, + endColor: Colors.green.shade200, + iconBgColor: Colors.green.withOpacity(0.2), + iconColor: Colors.green.shade600, + agentId: 'simultaneous_interpreter', + targetRoute: Routes.translation, ), AIFeature( title: 'AI女友', @@ -62,24 +81,19 @@ class ExploreController extends GetxController { endColor: Colors.pink.shade200, iconBgColor: Colors.pink.withOpacity(0.2), iconColor: Colors.pink.shade600, + agentId: 'cyber_girlfriend', + targetRoute: Routes.chat, ), AIFeature( - title: '音乐创作', - description: '用AI创作专属音乐', - icon: Icons.music_note, - startColor: Colors.green.shade100, - endColor: Colors.green.shade200, - iconBgColor: Colors.green.withOpacity(0.2), - iconColor: Colors.green.shade600, - ), - AIFeature( - title: '声音克隆', - description: '创建你的数字声音', - icon: Icons.mic, + title: '健康顾问', + description: '提供科学健康建议', + icon: Icons.healing, startColor: Colors.amber.shade100, endColor: Colors.amber.shade200, iconBgColor: Colors.amber.withOpacity(0.2), iconColor: Colors.amber.shade600, + agentId: 'health_advisor', + targetRoute: Routes.chat, ), ]; @@ -129,13 +143,34 @@ class ExploreController extends GetxController { void onAIFeatureTap(int index) { if (index < 0 || index >= aiFeatures.length) return; - Get.snackbar( - '功能', - '您选择了: ${aiFeatures[index].title}', - snackPosition: SnackPosition.TOP, - ); + final feature = aiFeatures[index]; + final agent = AgentProvider.getAgentById(feature.agentId); + + if (agent == null) { + Get.snackbar( + '错误', + '未找到对应的AI助手', + snackPosition: SnackPosition.TOP, + ); + return; + } - // 实际应用中,这里应该跳转到对应的功能页面 + // 根据目标路由进行跳转 + if (feature.targetRoute == Routes.chat) { + // 跳转到聊天页面 + Get.toNamed(feature.targetRoute, arguments: { + 'agentId': agent.id, + 'agentName': agent.name, + 'agentVoice': agent.voice, + 'agentAvatar': agent.avatarUrl, + 'agentSubtitle': agent.description, + 'systemPrompt': agent.systemPrompt, + 'welcomeMessage': agent.welcomeMessage, + }); + } else if (feature.targetRoute == Routes.translation) { + // 跳转到翻译页面 + Get.toNamed(feature.targetRoute); + } } // 点击功能场景 diff --git a/lib/modules/home/controllers/home_controller.dart b/lib/modules/home/controllers/home_controller.dart index f29767595..5a2768c9c 100644 --- a/lib/modules/home/controllers/home_controller.dart +++ b/lib/modules/home/controllers/home_controller.dart @@ -4,7 +4,7 @@ import '../../../data/services/bluetooth_service.dart'; import '../../../core/utils/logger.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart' hide BluetoothService; import 'package:flutter/material.dart'; - +import '../../../data/services/voice_interaction_service.dart'; // 连接状态枚举 enum ConnectionStatus { disconnected, // 未连接 @@ -50,17 +50,70 @@ class HomeController extends GetxController { @override void onInit() { super.onInit(); - // 检查是否有传入的蓝牙设备信息 - if (Get.arguments != null && Get.arguments is Map) { - _deviceInfo.assignAll(Get.arguments); - _updateConnectionStatus(); - } - // 获取蓝牙服务 - _initBluetoothService(); + // 立即加载缓存的设备信息并显示 + _loadCachedDeviceInfo(); - // 模拟定期获取电池状态 - _simulateBatteryUpdates(); + // 先进行简单初始化,避免立即进行蓝牙操作 + _initBasicServices(); + + // 延迟初始化蓝牙相关功能,让UI先渲染完成 + Future.delayed(const Duration(milliseconds: 1200), () { + _initBluetoothService(); + }); + } + + // 立即加载缓存的设备信息 + void _loadCachedDeviceInfo() { + try { + // 1. 首先检查是否有传入的设备信息(从PairingController传入) + if (Get.arguments != null && Get.arguments is Map) { + Logger.info('使用传入的设备信息'); + _deviceInfo.assignAll(Get.arguments); + _updateConnectionStatus(); + update(); // 立即更新UI + return; + } + + // 2. 如果没有传入信息,尝试从BluetoothService获取上次连接的设备信息 + if (Get.isRegistered()) { + BluetoothService bluetoothService = Get.find(); + if (bluetoothService.lastConnectedDeviceInfo.isNotEmpty) { + Logger.info('使用BluetoothService中存储的上次连接设备信息'); + _deviceInfo.assignAll(bluetoothService.lastConnectedDeviceInfo); + _updateConnectionStatus(); + update(); // 立即更新UI + return; + } + } + + // 3. 如果上述都没有,显示未连接状态 + Logger.info('没有找到缓存的设备信息,显示未连接状态'); + _connectionStatus.value = ConnectionStatus.disconnected; + _isConnected.value = false; + update(); // 立即更新UI + } catch (e) { + Logger.error('加载缓存设备信息失败: $e'); + } + } + + // 基础服务初始化,不涉及蓝牙等耗时操作 + void _initBasicServices() { + try { + // 初始化语音交互 + Future.microtask(() => VoiceInteractionService.to.initialize()); + + // 检查是否有传入的蓝牙设备信息 + if (Get.arguments != null && Get.arguments is Map) { + _deviceInfo.assignAll(Get.arguments); + _updateConnectionStatus(); + } + + // 模拟电池状态更新(不涉及设备通信) + _simulateBatteryUpdates(); + } catch (e) { + Logger.error('初始化基础服务失败: $e'); + } } // 初始化蓝牙服务 @@ -69,52 +122,220 @@ class HomeController extends GetxController { if (Get.isRegistered()) { _bluetoothService = Get.find(); - // 如果已经有连接的设备但没有设备信息 - if (_deviceInfo.isEmpty && _bluetoothService!.selectedDevice != null) { - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _updateConnectionStatus(); - } else if (_deviceInfo.isEmpty) { - // 如果没有设备信息,尝试获取已配对设备 - _checkPairedDevices(); - } + // 不再重复加载设备信息,因为已经在_loadCachedDeviceInfo中加载过了 + // 只进行状态同步 + + // 主动检查当前连接状态并更新 + _checkAndUpdateCurrentConnectionState(); // 监听连接状态变化 ever(_bluetoothService!.connectionStateObs, (state) { + Logger.info('蓝牙连接状态变化: $state'); _processConnectionStateChange(state); }); // 监听选中设备变化 ever(_bluetoothService!.selectedDeviceObs, (device) { + Logger.info('蓝牙选中设备变化: ${device?.platformName ?? "无设备"}'); if (device != null) { _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _updateConnectionStatus(); + // 通知UI刷新 + update(); } else if (_isConnected.value) { // 如果设备断开连接,更新状态 _isConnected.value = false; - _deviceInfo.clear(); + // 不清除设备信息,以便可以显示上次连接的设备 _connectionStatus.value = ConnectionStatus.disconnected; + // 通知UI刷新 + update(); } }); + + // 监听最后连接的设备信息变化 + ever(_bluetoothService!.lastConnectedDeviceInfoObs, (info) { + Logger.info('最后连接的设备信息变化'); + if (info.isNotEmpty) { + // 只有当设备信息为空或者连接状态不是已连接时,才更新设备信息 + if (_deviceInfo.isEmpty || _connectionStatus.value != ConnectionStatus.connected) { + _deviceInfo.assignAll(info); + _updateConnectionStatus(); + // 通知UI刷新 + update(); + } + } + }); + + // 如果当前没有连接状态,但有设备信息,尝试在后台重新连接 + if (_connectionStatus.value != ConnectionStatus.connected && + _deviceInfo.isNotEmpty && + _deviceInfo['device'] != null && + _deviceInfo['device']['id'] != null) { + // 延迟后台尝试连接设备,不阻塞UI + Future.delayed(const Duration(milliseconds: 2000), () { + _reconnectToLastDevice(); + }); + } + // 如果没有设备信息,延迟尝试获取已配对设备 + else if (_deviceInfo.isEmpty) { + // 这个操作放在最后并延迟执行,因为它可能会触发蓝牙扫描 + Future.delayed(const Duration(milliseconds: 2500), () { + _checkPairedDevices(); + }); + } } } catch (e) { Logger.error('获取蓝牙服务失败: $e'); - _connectionStatus.value = ConnectionStatus.failed; _connectionErrorMsg.value = '获取蓝牙服务失败'; + // 通知UI刷新 + update(); + } + } + + // 检查并更新当前连接状态 + void _checkAndUpdateCurrentConnectionState() { + if (_bluetoothService != null && _bluetoothService!.selectedDevice != null) { + // 如果有选择的设备,使用可靠方法获取一次状态 + _bluetoothService!.getReliableConnectionState(_bluetoothService!.selectedDevice!).then((state) { + Logger.info('主动检查设备连接状态: $state'); + _processConnectionStateChange(state); + }).catchError((e) { + Logger.error('检查连接状态失败: $e'); + }); + } + } + + // 后台尝试重新连接到上次的设备 + Future _reconnectToLastDevice() async { + try { + Logger.info('尝试重新连接到上次的设备'); + _connectionStatus.value = ConnectionStatus.connecting; + update(); // 立即更新UI显示连接中状态 + + // 使用超时机制,防止无响应 + return await Future.sync(() async { + // 获取设备ID + String deviceId = _deviceInfo['device']['id']; + Logger.info('上次连接的设备ID: $deviceId'); + + // 获取已配对设备,使用超时保护 + Logger.info('开始获取系统已配对设备列表...'); + List pairedDevices = await _bluetoothService!.getPairedDevices() + .timeout(const Duration(seconds: 5), onTimeout: () { + Logger.error('获取已配对设备超时'); + return []; + }); + + // 记录配对设备 + if (pairedDevices.isNotEmpty) { + Logger.info('发现 ${pairedDevices.length} 个已配对设备:'); + for (var i = 0; i < pairedDevices.length; i++) { + Logger.info('配对设备 #$i: ${pairedDevices[i].platformName ?? "未命名设备"} (${pairedDevices[i].remoteId.str})'); + } + } else { + Logger.info('未找到任何已配对设备'); + } + + // 查找匹配ID的设备 + Logger.info('开始查找匹配ID的设备: $deviceId'); + BluetoothDevice? targetDevice; + for (var device in pairedDevices) { + Logger.info('检查设备 ${device.platformName ?? "未命名设备"} (${device.remoteId.str}) 是否匹配'); + if (device.remoteId.str == deviceId) { + Logger.info('找到匹配ID的设备: ${device.platformName ?? "未命名设备"}'); + targetDevice = device; + break; + } + } + + // 如果找到目标设备,尝试连接 + if (targetDevice != null) { + Logger.info('找到目标设备: ${targetDevice.platformName ?? "未命名设备"},尝试连接'); + + // 直接尝试连接设备,不做复杂状态检查 + Logger.info('开始连接设备 ${targetDevice?.platformName ?? "未命名设备"}'); + final success = await _bluetoothService!.connectToDevice(targetDevice) + .timeout(const Duration(seconds: 5), onTimeout: () { + Logger.error('连接设备 ${targetDevice?.platformName ?? "未命名设备"} 超时'); + return false; + }); + + if (success) { + Logger.info('重新连接到设备 ${targetDevice?.platformName ?? "未命名设备"} 成功'); + _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); + _connectionStatus.value = ConnectionStatus.connected; + _isConnected.value = true; + _updateConnectionStatus(); + update(); // 立即更新UI + } else { + Logger.error('重新连接到设备 ${targetDevice?.platformName ?? "未命名设备"} 失败'); + _connectionStatus.value = ConnectionStatus.failed; + _connectionErrorMsg.value = '重新连接设备失败,请手动刷新或重新连接'; + update(); // 立即更新UI + } + } else { + Logger.info('未找到匹配ID的设备,尝试连接第一个配对设备'); + // 如果没有找到目标设备但有其他配对设备,尝试连接第一个 + if (pairedDevices.isNotEmpty) { + var firstDevice = pairedDevices.first; + Logger.info('尝试连接第一个配对设备: ${firstDevice.platformName ?? "未命名设备"}'); + + // 直接尝试连接设备 + Logger.info('开始连接设备 ${firstDevice.platformName ?? "未命名设备"}'); + final success = await _bluetoothService!.connectToDevice(firstDevice) + .timeout(const Duration(seconds: 5), onTimeout: () { + Logger.error('连接设备 ${firstDevice.platformName ?? "未命名设备"} 超时'); + return false; + }); + + if (success) { + Logger.info('自动连接到已配对设备成功: ${firstDevice.platformName ?? "未命名设备"}'); + _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); + _connectionStatus.value = ConnectionStatus.connected; + _isConnected.value = true; // 直接设置连接状态 + _updateConnectionStatus(); + update(); // 立即更新UI + } else { + Logger.error('连接到第一个配对设备 ${firstDevice.platformName ?? "未命名设备"} 失败'); + _connectionStatus.value = ConnectionStatus.failed; + _connectionErrorMsg.value = '连接设备失败,请重试'; + update(); // 立即更新UI + } + } else { + Logger.info('没有找到任何可用的配对设备'); + _connectionStatus.value = ConnectionStatus.disconnected; + update(); // 立即更新UI + } + } + }).timeout(const Duration(seconds: 15), onTimeout: () { + Logger.error('重新连接设备整体流程超时'); + _connectionStatus.value = ConnectionStatus.failed; + _connectionErrorMsg.value = '连接超时,请手动刷新或重新连接'; + update(); // 立即更新UI + }); + } catch (e) { + Logger.error('重新连接到设备失败: $e'); + _connectionStatus.value = ConnectionStatus.failed; + _connectionErrorMsg.value = '连接失败,请手动刷新或重新连接'; + update(); // 立即更新UI } } // 处理连接状态变化 void _processConnectionStateChange(BluetoothConnectionState state) { + // 强制从蓝牙服务获取最新设备信息 _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); switch (state) { case BluetoothConnectionState.connected: _connectionStatus.value = ConnectionStatus.connected; _connectionErrorMsg.value = ''; - _showConnectionSuccessMessage(); + _isConnected.value = true; // 直接设置连接状态为true + // _showConnectionSuccessMessage(); break; case BluetoothConnectionState.disconnected: _connectionStatus.value = ConnectionStatus.disconnected; + _isConnected.value = false; // 直接设置连接状态为false break; case BluetoothConnectionState.connecting: _connectionStatus.value = ConnectionStatus.connecting; @@ -124,7 +345,11 @@ class HomeController extends GetxController { break; } - _updateConnectionStatus(); + // 延迟一帧后刷新状态,确保UI能更新 + Future.microtask(() => _updateConnectionStatus()); + + // 通知UI刷新 + update(); } // 显示连接成功消息 @@ -147,70 +372,81 @@ class HomeController extends GetxController { Future _checkPairedDevices() async { try { if (_bluetoothService != null) { + Logger.info('开始检查已配对设备流程'); // 更新状态为正在连接 _connectionStatus.value = ConnectionStatus.connecting; + update(); // 立即更新UI // 如果已有选中的设备并且已连接,直接使用 if (_bluetoothService!.selectedDevice != null && _bluetoothService!.connectionState == BluetoothConnectionState.connected) { - Logger.info('使用已连接的设备: ${_bluetoothService!.selectedDevice!.platformName}'); + Logger.info('使用已连接的设备: ${_bluetoothService!.selectedDevice!.platformName}, 连接状态: ${_bluetoothService!.connectionState}'); _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _connectionStatus.value = ConnectionStatus.connected; + _isConnected.value = true; // 直接设置连接状态 _updateConnectionStatus(); + update(); // 立即更新UI return; } // 获取已配对设备 - final pairedDevices = await _bluetoothService!.getPairedDevices(); + Logger.info('开始获取系统已配对设备列表...'); + final pairedDevices = await _bluetoothService!.getPairedDevices() + .timeout(const Duration(seconds: 5), onTimeout: () { + Logger.error('获取已配对设备超时'); + return []; + }); + if (pairedDevices.isEmpty) { Logger.info('没有找到已配对设备'); _connectionStatus.value = ConnectionStatus.disconnected; + update(); // 立即更新UI return; } - // 遍历配对设备,查找已连接的设备 - for (var device in pairedDevices) { - try { - final state = await device.connectionState.first; - if (state == BluetoothConnectionState.connected) { - // 找到已连接的设备,更新UI - final success = await _bluetoothService!.connectToDevice(device); - if (success) { - Logger.info('使用已连接的已配对设备: ${device.platformName}'); - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _connectionStatus.value = ConnectionStatus.connected; - _updateConnectionStatus(); - return; - } - } - } catch (e) { - // 忽略单个设备检查错误 - Logger.error('检查设备连接状态失败: $e'); - } + Logger.info('发现 ${pairedDevices.length} 个已配对设备'); + for (var i = 0; i < pairedDevices.length; i++) { + var device = pairedDevices[i]; + Logger.info('已配对设备 #$i: ${device.platformName} (${device.remoteId.str})'); } - // 如果没有已连接的设备,尝试连接第一个配对设备 + // 遍历配对设备,检查是否有需要连接的设备 + Logger.info('开始查找设备进行连接...'); if (pairedDevices.isNotEmpty) { - Logger.info('尝试连接第一个配对设备: ${pairedDevices.first.platformName}'); - final success = await _bluetoothService!.connectToDevice(pairedDevices.first); + // 选择第一个配对设备进行连接 + var deviceToConnect = pairedDevices.first; + Logger.info('尝试连接配对设备: ${deviceToConnect.platformName ?? "未命名设备"}'); + + final success = await _bluetoothService!.connectToDevice(deviceToConnect) + .timeout(const Duration(seconds: 5), onTimeout: () { + Logger.error('连接设备 ${deviceToConnect.platformName ?? "未命名设备"} 超时'); + return false; + }); + if (success) { - Logger.info('自动连接到已配对设备: ${pairedDevices.first.platformName}'); + Logger.info('成功连接到设备: ${deviceToConnect.platformName ?? "未命名设备"}'); _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _connectionStatus.value = ConnectionStatus.connected; + _isConnected.value = true; _updateConnectionStatus(); + update(); // 立即更新UI } else { // 连接失败 + Logger.error('连接设备 ${deviceToConnect.platformName ?? "未命名设备"} 失败'); _connectionStatus.value = ConnectionStatus.failed; _connectionErrorMsg.value = '连接设备失败,请重试'; + update(); // 立即更新UI } } else { _connectionStatus.value = ConnectionStatus.disconnected; + update(); // 立即更新UI } } } catch (e) { Logger.error('检查已配对设备失败: $e'); _connectionStatus.value = ConnectionStatus.failed; _connectionErrorMsg.value = '检查设备失败: ${e.toString()}'; + update(); // 立即更新UI } } @@ -369,13 +605,18 @@ class HomeController extends GetxController { return; } - // 设置正在刷新状态 + // 设置正在刷新状态,但保留当前设备信息显示 _connectionStatus.value = ConnectionStatus.connecting; + update(); // 立即更新UI状态 + + // 强制检查当前蓝牙连接状态 + _checkAndUpdateCurrentConnectionState(); if (_bluetoothService!.selectedDevice != null) { // 如果有选中的设备,刷新设备信息 _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _updateConnectionStatus(); + update(); // 确保UI更新 // 检查刷新后的连接状态 if (_isConnected.value) { @@ -387,19 +628,22 @@ class HomeController extends GetxController { colorText: Colors.green[800], ); } else { - // 设备可能已断开连接 - _connectionStatus.value = ConnectionStatus.disconnected; - Get.snackbar( - '提示', - '设备已断开连接', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.amber[100], - colorText: Colors.amber[800], - ); - - // 尝试重新连接 - await _checkPairedDevices(); + // 设备可能已断开连接,尝试使用存储的设备信息重新连接 + if (_bluetoothService!.lastConnectedDeviceInfo.isNotEmpty) { + _connectionStatus.value = ConnectionStatus.disconnected; + update(); // 更新状态 + _reconnectToLastDevice(); + } else { + // 上次连接的设备信息也为空,尝试连接已配对设备 + await _checkPairedDevices(); + } } + } else if (_bluetoothService!.lastConnectedDeviceInfo.isNotEmpty) { + // 如果没有选中的设备但有上次连接的设备信息,尝试重新连接 + _deviceInfo.assignAll(_bluetoothService!.lastConnectedDeviceInfo); + _updateConnectionStatus(); + update(); // 确保UI更新 + _reconnectToLastDevice(); } else { // 如果没有选中的设备,尝试连接已配对设备 await _checkPairedDevices(); @@ -426,6 +670,7 @@ class HomeController extends GetxController { Logger.error('刷新设备信息失败: $e'); _connectionStatus.value = ConnectionStatus.failed; _connectionErrorMsg.value = '刷新设备信息失败: ${e.toString()}'; + update(); // 确保状态更新 Get.snackbar( '提示', diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index c4fce6a9e..1ce7d347d 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -49,9 +49,9 @@ class PairingController extends GetxController { // 启动搜索动画 _startSearchingAnimation(); - // 延迟初始化蓝牙服务,避免在应用启动时就请求权限 + // 延迟更久初始化蓝牙服务,避免在应用启动时就请求权限和阻塞主线程 // 改为延迟初始化,给UI足够时间渲染 - Future.delayed(const Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 1500), () { _initBluetoothService(); }); } @@ -69,6 +69,7 @@ class PairingController extends GetxController { if (Get.isRegistered()) { _bluetoothService = Get.find(); } else { + // 在后台初始化蓝牙服务 _bluetoothService = await Get.put(BluetoothService()).init(); } @@ -82,8 +83,8 @@ class PairingController extends GetxController { // 如果蓝牙开启,开始扫描设备 if (on) { - // 延迟开始扫描,避免权限请求过早 - Future.delayed(const Duration(milliseconds: 300), () { + // 延迟开始扫描,避免权限请求过早和主线程阻塞 + Future.delayed(const Duration(milliseconds: 800), () { _startScanningDevices(); }); } else { @@ -96,8 +97,8 @@ class PairingController extends GetxController { // 如果蓝牙已经开启,直接开始扫描 if (_bluetoothService.isOn) { _isBluetoothOn.value = true; - // 延迟开始扫描,避免权限请求过早 - Future.delayed(const Duration(milliseconds: 300), () { + // 延迟开始扫描,避免权限请求过早和主线程阻塞 + Future.delayed(const Duration(milliseconds: 800), () { _startScanningDevices(); }); } else { @@ -155,116 +156,66 @@ class PairingController extends GetxController { // 开始扫描蓝牙设备 Future _startScanningDevices() async { try { - // 先检查是否已有连接的设备 - if (_bluetoothService.connectedDevices.isNotEmpty) { - // 有已连接的设备,直接使用 - _deviceFound(_bluetoothService.connectedDevices.first); + _searchStatus.value = SearchStatus.searching; + + // 先检查是否有上次连接的设备信息 + if (_bluetoothService.lastConnectedDeviceInfo.isNotEmpty && + _bluetoothService.lastConnectedDeviceInfo['connected'] == true) { + Logger.info('使用上次连接的设备信息'); + _deviceInfo.value = _bluetoothService.lastConnectedDeviceInfo; + _onDeviceFound(); return; } - // 检查是否有已配对的设备 - if (await _checkPairedDevices()) { - // 已找到并成功连接到配对设备 + // 使用BluetoothService的自动连接功能查找设备 + Logger.info('尝试自动连接设备'); + BluetoothDevice? device = await _bluetoothService.autoConnectDevice() + .timeout(const Duration(seconds: 8), onTimeout: () { + Logger.info('自动连接设备超时'); + return null; + }); + + if (device != null) { + Logger.info('自动连接设备成功: ${device.platformName}'); + _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); + _onDeviceFound(); return; } - // 搜索新设备 - _searchStatus.value = SearchStatus.searching; - await _bluetoothService.startScan(timeout: const Duration(seconds: 10)); + // 如果自动连接失败,尝试扫描新设备 + Logger.info('自动连接失败,开始扫描新设备'); + device = await _bluetoothService.scanAndConnectToDevice( + timeout: const Duration(seconds: 5) + ).timeout(const Duration(seconds: 6), onTimeout: () { + Logger.info('扫描并连接设备超时'); + return null; + }); - // 检查扫描结果 - if (_bluetoothService.scanResults.isNotEmpty) { - // 找到设备,选择信号最强的一个 - final strongestSignal = _bluetoothService.scanResults - .reduce((a, b) => (a.rssi > b.rssi) ? a : b); - - // 连接设备 - final success = await _bluetoothService.connectToDevice(strongestSignal.device); - - if (success) { - _deviceFound(strongestSignal.device); - } else { - _errorMessage.value = '连接设备失败,请重试'; - _searchStatus.value = SearchStatus.failed; - _stopSearchingAnimation(); - } + if (device != null) { + Logger.info('扫描并连接设备成功: ${device.platformName}'); + _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); + _onDeviceFound(); } else { - // 未找到设备 + // 未找到任何设备 _errorMessage.value = '未找到蓝牙设备,请确保设备已开启并在附近'; _searchStatus.value = SearchStatus.failed; _stopSearchingAnimation(); } } catch (e) { - Logger.error('扫描蓝牙设备失败: $e'); - _errorMessage.value = '扫描蓝牙设备失败: ${e.toString()}'; + Logger.error('设备搜索和连接失败: $e'); + _errorMessage.value = '设备搜索失败: ${e.toString()}'; _searchStatus.value = SearchStatus.failed; _stopSearchingAnimation(); } } - // 检查已配对设备 - Future _checkPairedDevices() async { - try { - // 先检查当前是否有设备已连接 - if (_bluetoothService.selectedDevice != null && - _bluetoothService.connectionState == BluetoothConnectionState.connected) { - Logger.info('已有设备连接: ${_bluetoothService.selectedDevice!.platformName}'); - _deviceFound(_bluetoothService.selectedDevice!); - return true; - } - - List pairedDevices = await _bluetoothService.getPairedDevices(); - if (pairedDevices.isEmpty) { - Logger.info('没有找到已配对设备'); - return false; - } - - // 遍历配对设备,检查是否有设备已连接 - for (BluetoothDevice device in pairedDevices) { - try { - BluetoothConnectionState state = await device.connectionState.first; - if (state == BluetoothConnectionState.connected) { - Logger.info('发现已连接的设备: ${device.platformName}'); - // 设置为当前设备但不需要连接 - final success = await _bluetoothService.connectToDevice(device); - if (success) { - _deviceFound(device); - return true; - } - } - } catch (e) { - // 忽略单个设备的连接状态检查错误,继续检查下一个 - Logger.error('检查设备连接状态失败: $e'); - } - } - - // 如果没有找到已连接的设备,尝试连接第一个配对设备 - Logger.info('尝试连接配对设备: ${pairedDevices.first.platformName}'); - final success = await _bluetoothService.connectToDevice(pairedDevices.first); - - if (success) { - _deviceFound(pairedDevices.first); - return true; - } else { - // 连接失败,继续搜索新设备 - Logger.info('已配对设备连接失败,继续搜索新设备'); - return false; - } - } catch (e) { - Logger.error('检查已配对设备失败: $e'); - return false; - } - } - - // 设备被找到时的处理 - void _deviceFound(BluetoothDevice device) { + // 设备找到并连接成功后的处理 + void _onDeviceFound() { _searchStatus.value = SearchStatus.found; _stopSearchingAnimation(); - // 获取设备信息 - _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); - - // 延迟后跳转到主页,同时传递设备信息 + // 更新日志并延迟跳转到主页 + Logger.info('设备连接成功,准备跳转到主页'); Timer(const Duration(seconds: 1), () { Get.offAllNamed(Routes.home, arguments: _deviceInfo.value); }); @@ -272,24 +223,25 @@ class PairingController extends GetxController { // 手动连接 void manualConnect() { - if (_searchStatus.value == SearchStatus.searching) { - // 如果正在搜索,则停止搜索 - _bluetoothService.stopScan(); - } + // 停止当前扫描(如果有) + _bluetoothService.stopScan(); - // 重新开始搜索 + // 更新UI状态 _startSearchingAnimation(); _searchStatus.value = SearchStatus.searching; + _errorMessage.value = ''; + + // 直接开始扫描并连接 _startScanningDevices(); } // 稍后再说 void skipConnecting() { - if (_searchStatus.value == SearchStatus.searching) { - // 如果正在搜索,则停止搜索 - _bluetoothService.stopScan(); - } + // 停止当前扫描 + _bluetoothService.stopScan(); + _stopSearchingAnimation(); + // 直接跳转到主页 Get.offAllNamed(Routes.home); } } \ No newline at end of file