Browse Source

bluteeth

weicu
wolfplus 2 years ago
parent
commit
13003cd286
  1. 30
      android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt
  2. 270
      lib/data/services/bluetooth_service.dart
  3. 5
      lib/modules/chat/controllers/chat_controller.dart
  4. 755
      lib/modules/chat/views/chat_view.dart
  5. 71
      lib/modules/explore/controllers/explore_controller.dart
  6. 351
      lib/modules/home/controllers/home_controller.dart
  7. 156
      lib/modules/pairing/controllers/pairing_controller.dart

30
android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt

@ -71,7 +71,7 @@ class MainActivity: FlutterActivity() {
// Azure 语音服务配置 // Azure 语音服务配置
var azureSpeechKey: String = "2K2VOHSuL8ZSEnbKgHSyTwo1ohj7C2gbxyVimsjpVUt6aETxVwlvJQQJ99BCACqBBLyXJ3w3AAAYACOGqCMj" var azureSpeechKey: String = "2K2VOHSuL8ZSEnbKgHSyTwo1ohj7C2gbxyVimsjpVUt6aETxVwlvJQQJ99BCACqBBLyXJ3w3AAAYACOGqCMj"
var azureSpeechRegion: String = "southeastasia" var azureSpeechRegion: String = "southeastasia"
var volcanoAiApiKey: String = "0fd7c93b-b9bf-4b0b-bca6-e284491792bf" var volcanoAiApiKey: String = "168deb3d-fd0c-4912-b9f1-aaee5c6743e6"
// 标记是否已初始化 // 标记是否已初始化
var isAzureHelpersInitialized = false var isAzureHelpersInitialized = false
@ -158,20 +158,20 @@ class MainActivity: FlutterActivity() {
events?.success(mapOf("type" to "channelReady")) events?.success(mapOf("type" to "channelReady"))
Log.d(TAG, "已发送 channelReady 事件") Log.d(TAG, "已发送 channelReady 事件")
// 测试发送一个聊天记录事件 // // 测试发送一个聊天记录事件
Handler(Looper.getMainLooper()).postDelayed({ // Handler(Looper.getMainLooper()).postDelayed({
if (voiceInteractionEventSink != null) { // if (voiceInteractionEventSink != null) {
Log.d(TAG, "发送测试聊天记录事件") // Log.d(TAG, "发送测试聊天记录事件")
sendChatHistoryEvent( // sendChatHistoryEvent(
"personal_assistant", // "personal_assistant",
"这是一条测试用户消息", // "这是一条测试用户消息",
"这是一条测试助手回复", // "这是一条测试助手回复",
System.currentTimeMillis() // System.currentTimeMillis()
) // )
} else { // } else {
Log.e(TAG, "无法发送测试事件:事件通道未准备好") // Log.e(TAG, "无法发送测试事件:事件通道未准备好")
} // }
}, 5000) // 5秒后发送测试事件 // }, 5000) // 5秒后发送测试事件
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "发送 channelReady 事件失败: ${e.message}", e) Log.e(TAG, "发送 channelReady 事件失败: ${e.message}", e)
} }

270
lib/data/services/bluetooth_service.dart

@ -34,6 +34,9 @@ class BluetoothService extends GetxService {
// 连接状态 // 连接状态
final _connectionState = Rx<BluetoothConnectionState>(BluetoothConnectionState.disconnected); final _connectionState = Rx<BluetoothConnectionState>(BluetoothConnectionState.disconnected);
// 存储最后连接的设备信息,供其他控制器使用
final _lastConnectedDeviceInfo = Rx<Map<String, dynamic>>({});
// 订阅 // 订阅
StreamSubscription? _stateSubscription; StreamSubscription? _stateSubscription;
StreamSubscription? _scanSubscription; StreamSubscription? _scanSubscription;
@ -51,6 +54,7 @@ class BluetoothService extends GetxService {
List<ScanResult> get scanResults => _scanResults; List<ScanResult> get scanResults => _scanResults;
BluetoothDevice? get selectedDevice => _selectedDevice.value; BluetoothDevice? get selectedDevice => _selectedDevice.value;
BluetoothConnectionState get connectionState => _connectionState.value; BluetoothConnectionState get connectionState => _connectionState.value;
Map<String, dynamic> get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value;
// Observable getters (for reactive programming) // Observable getters (for reactive programming)
RxBool get isAvailableObs => _isAvailable; RxBool get isAvailableObs => _isAvailable;
@ -59,6 +63,7 @@ class BluetoothService extends GetxService {
RxList<ScanResult> get scanResultsObs => _scanResults; RxList<ScanResult> get scanResultsObs => _scanResults;
Rx<BluetoothDevice?> get selectedDeviceObs => _selectedDevice; Rx<BluetoothDevice?> get selectedDeviceObs => _selectedDevice;
Rx<BluetoothConnectionState> get connectionStateObs => _connectionState; Rx<BluetoothConnectionState> get connectionStateObs => _connectionState;
Rx<Map<String, dynamic>> get lastConnectedDeviceInfoObs => _lastConnectedDeviceInfo;
/// 初始化蓝牙服务 /// 初始化蓝牙服务
Future<BluetoothService> init() async { Future<BluetoothService> init() async {
@ -66,6 +71,8 @@ class BluetoothService extends GetxService {
// 不在初始化时检查权限,改为在需要时才检查 // 不在初始化时检查权限,改为在需要时才检查
// await _checkAndRequestPermissions(); // await _checkAndRequestPermissions();
// 在后台线程初始化蓝牙状态监听,避免主线程阻塞
Future.microtask(() {
// 监听蓝牙状态 // 监听蓝牙状态
_stateSubscription = FlutterBluePlus.adapterState.listen((state) { _stateSubscription = FlutterBluePlus.adapterState.listen((state) {
_isOn.value = state == BluetoothAdapterState.on; _isOn.value = state == BluetoothAdapterState.on;
@ -74,21 +81,40 @@ class BluetoothService extends GetxService {
if (state == BluetoothAdapterState.on) { if (state == BluetoothAdapterState.on) {
// 蓝牙打开时,获取已连接设备,但不请求权限 // 蓝牙打开时,获取已连接设备,但不请求权限
// 只有在用户主动扫描时才请求权限 // 只有在用户主动扫描时才请求权限
_getConnectedDevices(); // 在后台线程执行,避免主线程阻塞
Future.microtask(() => _getConnectedDevices());
} else if (state == BluetoothAdapterState.off) { } else if (state == BluetoothAdapterState.off) {
// 蓝牙关闭时,清空设备列表 // 蓝牙关闭时,清空设备列表
_connectedDevices.clear(); _connectedDevices.clear();
_scanResults.clear(); _scanResults.clear();
_selectedDevice.value = null; _selectedDevice.value = null;
_lastConnectedDeviceInfo.value = {};
} }
}); });
});
// 不在初始化时立即检查蓝牙状态,而是延迟到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;
if (await FlutterBluePlus.isOn) { _isAvailable.value = isOn;
_isOn.value = true;
_isAvailable.value = true; if (isOn) {
_getConnectedDevices(); // 在后台线程获取设备,避免主线程阻塞
Future.microtask(() => _getConnectedDevices());
}
} catch (e) {
Logger.error('延迟检查蓝牙状态失败: $e');
} }
});
return this; return this;
} catch (e) { } catch (e) {
@ -114,14 +140,21 @@ class BluetoothService extends GetxService {
/// 获取已连接的设备 /// 获取已连接的设备
Future<void> _getConnectedDevices() async { Future<void> _getConnectedDevices() async {
try { try {
// 获取已连接的设备 // 使用超时机制,避免无限等待
List<BluetoothDevice> devices = await FlutterBluePlus.connectedSystemDevices; List<BluetoothDevice> devices = await FlutterBluePlus.connectedSystemDevices
.timeout(const Duration(seconds: 3), onTimeout: () {
Logger.error('获取已连接设备超时');
return <BluetoothDevice>[];
});
_connectedDevices.value = devices; _connectedDevices.value = devices;
// 如果有已连接的设备但没有选中设备,则选中第一个 // 如果有已连接的设备但没有选中设备,则选中第一个
if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) { if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) {
_selectedDevice.value = _connectedDevices[0]; _selectedDevice.value = _connectedDevices[0];
_monitorConnectionState(_selectedDevice.value!); _monitorConnectionState(_selectedDevice.value!);
// 更新最后连接的设备信息
_updateLastConnectedDeviceInfo();
} }
Logger.info('已连接设备数量: ${_connectedDevices.length}'); Logger.info('已连接设备数量: ${_connectedDevices.length}');
@ -142,15 +175,17 @@ class BluetoothService extends GetxService {
} }
} }
/// 开始扫描附近的蓝牙设备 /// 开始扫描附近的蓝牙设备并尝试连接最强信号设备
Future<void> startScan({Duration? timeout}) async { Future<BluetoothDevice?> scanAndConnectToDevice({Duration? timeout}) async {
if (_isScanning.value) return; if (_isScanning.value) {
await stopScan();
}
// 清空旧的扫描结果 // 清空旧的扫描结果
_scanResults.clear(); _scanResults.clear();
try { try {
// 开始扫描 // 设置扫描监听
_scanSubscription?.cancel(); _scanSubscription?.cancel();
_scanSubscription = FlutterBluePlus.scanResults.listen((results) { _scanSubscription = FlutterBluePlus.scanResults.listen((results) {
_scanResults.value = results; _scanResults.value = results;
@ -161,16 +196,34 @@ class BluetoothService extends GetxService {
// 开始扫描 // 开始扫描
await FlutterBluePlus.startScan( await FlutterBluePlus.startScan(
timeout: timeout ?? const Duration(seconds: 10), timeout: timeout ?? const Duration(seconds: 5),
); );
// 扫描完成后更新状态 // 扫描完成
_isScanning.value = false; _isScanning.value = false;
Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备'); Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备');
// 如果找到设备,尝试连接信号最强的
if (_scanResults.isNotEmpty) {
// 按信号强度排序
List<ScanResult> 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) { } catch (e) {
_isScanning.value = false; _isScanning.value = false;
Logger.error('蓝牙扫描失败: $e'); Logger.error('蓝牙扫描或连接失败: $e');
return null;
} }
} }
@ -186,66 +239,56 @@ class BluetoothService extends GetxService {
} }
} }
/// 连接到指定设备 /// 连接到指定的蓝牙设备 - 简化版本
Future<bool> connectToDevice(BluetoothDevice device) async { Future<bool> connectToDevice(BluetoothDevice device) async {
if (_selectedDevice.value == device) { Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})');
// 如果是当前选中设备,检查连接状态
if (_connectionState.value == BluetoothConnectionState.connected) {
Logger.info('设备 ${device.platformName} 已经连接,无需重新连接');
return true;
}
}
try { try {
// 检查设备是否已连接 // 如果是当前已连接设备,直接返回成功
bool isAlreadyConnected = false; if (_selectedDevice.value == device &&
// 检查设备连接状态 _connectionState.value == BluetoothConnectionState.connected) {
BluetoothConnectionState state = await device.connectionState.first; Logger.info('设备已连接,无需重新连接');
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);
return true; return true;
} }
// 断开当前连接 // 断开当前连接
if (_selectedDevice.value != null && _selectedDevice.value != device) {
await disconnectFromDevice(); await disconnectFromDevice();
}
// 连接新设备 // 直接尝试连接
Logger.info('直接连接设备 ${device.platformName}');
await device.connect(); await device.connect();
// 更新状态 // 更新设备信息
_selectedDevice.value = device; _selectedDevice.value = device;
_monitorConnectionState(device); _monitorConnectionState(device);
_updateLastConnectedDeviceInfo();
Logger.info('设备 ${device.platformName} 连接成功');
return true; return true;
} catch (e) { } catch (e) {
Logger.error('连接蓝牙设备失败: $e'); Logger.error('连接设备 ${device.platformName} 失败: $e');
return false; return false;
} }
} }
/// 断开与当前设备的连接 /// 断开与当前设备的连接
Future<bool> disconnectFromDevice() async { Future<bool> disconnectFromDevice() async {
if (_selectedDevice.value == null) return true; if (_selectedDevice.value == null) {
Logger.info('没有当前选中的设备,无需断开连接');
return true;
}
try { try {
Logger.info('正在断开设备 ${_selectedDevice.value!.platformName} 的连接');
await _selectedDevice.value!.disconnect(); await _selectedDevice.value!.disconnect();
Logger.info('设备连接已断开,取消连接状态监听');
_connectionSubscription?.cancel(); _connectionSubscription?.cancel();
_connectionSubscription = null; _connectionSubscription = null;
_selectedDevice.value = null; _selectedDevice.value = null;
// 清除最后连接的设备信息
_lastConnectedDeviceInfo.value = {};
return true; return true;
} catch (e) { } catch (e) {
@ -256,14 +299,35 @@ class BluetoothService extends GetxService {
/// 监控连接状态 /// 监控连接状态
void _monitorConnectionState(BluetoothDevice device) { void _monitorConnectionState(BluetoothDevice device) {
_connectionSubscription?.cancel(); Logger.info('设置设备 ${device.platformName} 的连接状态监听');
// 取消之前的监听
if (_connectionSubscription != null) {
Logger.info('取消之前的连接状态监听');
_connectionSubscription!.cancel();
_connectionSubscription = null;
}
// 设置新的监听
_connectionSubscription = device.connectionState.listen((state) { _connectionSubscription = device.connectionState.listen((state) {
Logger.info('设备 ${device.platformName} 连接状态变更为: $state');
_connectionState.value = state; _connectionState.value = state;
if (state == BluetoothConnectionState.disconnected) { if (state == BluetoothConnectionState.disconnected) {
Logger.info('设备 ${device.platformName} 已断开连接');
_selectedDevice.value = null; _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,29 @@ class BluetoothService extends GetxService {
}; };
} }
return { final deviceInfo = {
'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
}
};
// 更新最后连接的设备信息
_lastConnectedDeviceInfo.value = deviceInfo;
return deviceInfo;
}
/// 更新最后连接的设备信息
void _updateLastConnectedDeviceInfo() {
if (_selectedDevice.value != null) {
_lastConnectedDeviceInfo.value = {
'connected': _connectionState.value == BluetoothConnectionState.connected, 'connected': _connectionState.value == BluetoothConnectionState.connected,
'device': { 'device': {
'name': _selectedDevice.value!.platformName.isEmpty ? 'name': _selectedDevice.value!.platformName.isEmpty ?
@ -288,6 +374,90 @@ class BluetoothService extends GetxService {
} }
}; };
} }
}
/// 一个综合方法,执行自动查找并连接设备的完整流程
Future<BluetoothDevice?> autoConnectDevice() async {
try {
// 1. 先检查当前已连接设备
if (_selectedDevice.value != null &&
_connectionState.value == BluetoothConnectionState.connected) {
Logger.info('使用当前已连接设备: ${_selectedDevice.value!.platformName}');
return _selectedDevice.value;
}
// 2. 检查系统中已连接的设备
try {
List<BluetoothDevice> 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<BluetoothDevice> 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<BluetoothConnectionState> 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<BluetoothDevice> 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 @override
void onClose() { void onClose() {

5
lib/modules/chat/controllers/chat_controller.dart

@ -257,6 +257,8 @@ class ChatController extends GetxController {
Future<void> sendMessage(String text) async { Future<void> sendMessage(String text) async {
if (text.trim().isEmpty) return; if (text.trim().isEmpty) return;
_voiceInteractionService.pauseVoiceInteraction();
// 中断当前响应 // 中断当前响应
_interruptCurrentResponse("发送新消息,中断当前响应"); _interruptCurrentResponse("发送新消息,中断当前响应");
@ -420,6 +422,7 @@ class ChatController extends GetxController {
await _asrService.initialize(language: 'zh-CN'); await _asrService.initialize(language: 'zh-CN');
// 启动连续语音识别 // 启动连续语音识别
// 注意:在UI更新后,使用模拟波形动画替代实际麦克风输入的波形数据
final recognitionStream = await _asrService.startContinuousRecognition(); final recognitionStream = await _asrService.startContinuousRecognition();
isVoiceRecognitionActive.value = true; isVoiceRecognitionActive.value = true;
@ -429,6 +432,8 @@ class ChatController extends GetxController {
// 重置临时消息索引 // 重置临时消息索引
_tempMessageIndex = -1; _tempMessageIndex = -1;
// 监听识别事件 // 监听识别事件
_recognitionSubscription = recognitionStream.listen( _recognitionSubscription = recognitionStream.listen(
_handleRecognitionEvent, _handleRecognitionEvent,

755
lib/modules/chat/views/chat_view.dart

@ -8,115 +8,68 @@ import '../models/message_model.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
import '../../../core/widgets/message_bubble.dart'; import '../../../core/widgets/message_bubble.dart';
class ChatView extends StatefulWidget { class ChatView extends GetView<ChatController> {
const ChatView({Key? key}) : super(key: key); const ChatView({Key? key}) : super(key: key);
@override @override
State<ChatView> createState() => _ChatViewState(); Widget build(BuildContext context) {
} return Scaffold(
backgroundColor: Colors.grey[50], // 使用浅灰背景,类似HTML模板
class _ChatViewState extends State<ChatView> with TickerProviderStateMixin { appBar: _buildAppBar(),
// 获取控制器 body: _buildBody(),
ChatController get controller => Get.find<ChatController>();
// 添加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,
); );
} }
});
});
// 监听语音识别状态变化 // 构建AppBar
ever(controller.isVoiceRecognitionActive, (isActive) { PreferredSizeWidget _buildAppBar() {
if (isActive) { return AppBar(
// 开始录音以获取波形数据 elevation: 1,
_recorderController.record(); backgroundColor: Colors.white,
} else {
// 停止录音
_recorderController.stop();
}
});
}
// 初始化录音控制器
Future<void> _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( leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Colors.black), icon: Icon(Icons.arrow_back_ios, color: Colors.black87, size: 20.r),
onPressed: () => Get.back(), onPressed: () => Get.back(),
), ),
titleSpacing: 0,
title: Row( title: Row(
children: [ children: [
CircleAvatar( // AI头像
backgroundImage: AssetImage(controller.agent.avatarUrl), Container(
radius: 18.r, 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),
),
],
),
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: 10.w), ),
),
SizedBox(width: 12.w),
// AI名称和状态
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -127,19 +80,28 @@ class _ChatViewState extends State<ChatView> with TickerProviderStateMixin {
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.black, color: Colors.black87,
),
), ),
Row(
children: [
Container(
width: 6.r,
height: 6.r,
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
), ),
),
SizedBox(width: 4.w),
Text( Text(
controller.agent.description.length > 20 '在线',
? '${controller.agent.description.substring(0, 20)}...'
: controller.agent.description,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: Colors.grey, color: Colors.green,
),
), ),
maxLines: 1, ],
overflow: TextOverflow.ellipsis,
), ),
], ],
), ),
@ -160,8 +122,26 @@ class _ChatViewState extends State<ChatView> with TickerProviderStateMixin {
onPressed: controller.toggleTts, onPressed: controller.toggleTts,
)), )),
// 更多选项 // 更多选项
PopupMenuButton( Container(
icon: Icon(Icons.more_vert, color: Colors.black), 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),
),
itemBuilder: (context) => [ itemBuilder: (context) => [
PopupMenuItem( PopupMenuItem(
value: 'clear', value: 'clear',
@ -180,17 +160,97 @@ class _ChatViewState extends State<ChatView> with TickerProviderStateMixin {
} }
}, },
), ),
],
elevation: 0,
backgroundColor: Colors.white,
), ),
body: Container( ],
color: Colors.white, // 确保body背景也是白色 );
child: Column( }
// 构建主体内容
Widget _buildBody() {
return Column(
children: [ children: [
// 消息列表 // 消息列表
Expanded( Expanded(
child: Obx(() => ListView.builder( 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,
),
),
),
],
),
);
}
// 构建聊天列表
Widget _buildChatList() {
return ListView.builder(
controller: controller.scrollController, controller: controller.scrollController,
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h),
itemCount: controller.messages.length, itemCount: controller.messages.length,
@ -199,76 +259,237 @@ class _ChatViewState extends State<ChatView> with TickerProviderStateMixin {
cacheExtent: 1000, // 增加缓存范围,减少重建 cacheExtent: 1000, // 增加缓存范围,减少重建
itemBuilder: (context, index) { itemBuilder: (context, index) {
final message = controller.messages[index]; final message = controller.messages[index];
return MessageBubble( return _buildMessageItem(message);
key: ObjectKey(message), // 使用ObjectKey而不是ValueKey },
message: message, );
isUser: message.role == 'user', }
// 构建消息项
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 Flexible(
? _buildVoiceInputPanel() child: Container(
: _buildTextInputPanel()), 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<Color>(
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() { Widget _buildTextInputPanel() {
// 创建一个TextEditingController来控制输入框 final textController = TextEditingController(text: controller.inputText.value);
final TextEditingController textController = TextEditingController(text: controller.inputText.value);
// 监听输入变化
textController.addListener(() { textController.addListener(() {
controller.inputText.value = textController.text; controller.inputText.value = textController.text;
}); });
return Container( return Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withOpacity(0.06),
blurRadius: 5, blurRadius: 10,
offset: Offset(0, -1), offset: Offset(0, -1),
), ),
], ],
), ),
child: SafeArea(
child: Row( child: Row(
children: [ children: [
// 语音输入按钮 // 语音输入按钮
IconButton( 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( icon: Icon(
Icons.mic_none, Icons.mic_none,
color: Colors.grey, color: Colors.grey[600],
size: 20.r,
), ),
padding: EdgeInsets.zero,
onPressed: controller.startVoiceRecognition, onPressed: controller.startVoiceRecognition,
), ),
),
// 文本输入框 // 文本输入框
Expanded( Expanded(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.w),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(20.r),
),
child: TextField( child: TextField(
controller: textController, controller: textController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: '发消息...', hintText: '输入消息...',
border: OutlineInputBorder( hintStyle: TextStyle(color: Colors.grey[500]),
borderRadius: BorderRadius.circular(20.r), border: InputBorder.none,
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.grey[200],
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 16.w, horizontal: 16.w,
vertical: 8.h, vertical: 10.h,
), ),
), ),
maxLines: null, maxLines: null,
textInputAction: TextInputAction.send, textInputAction: TextInputAction.send,
style: TextStyle(
fontSize: 15.sp,
color: Colors.black87,
),
onSubmitted: (value) { onSubmitted: (value) {
if (value.trim().isNotEmpty) { if (value.trim().isNotEmpty) {
controller.sendMessage(value); controller.sendMessage(value);
@ -277,55 +498,109 @@ class _ChatViewState extends State<ChatView> with TickerProviderStateMixin {
}, },
), ),
), ),
),
// 发送按钮 // 发送按钮
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( icon: Icon(
Icons.send, Icons.send,
size: 18.r,
color: controller.inputText.value.trim().isEmpty color: controller.inputText.value.trim().isEmpty
? Colors.grey ? Colors.grey[500]
: AppColors.primary, : Colors.white,
), ),
padding: EdgeInsets.zero,
onPressed: controller.inputText.value.trim().isEmpty onPressed: controller.inputText.value.trim().isEmpty
? null ? null
: () { : () {
controller.sendMessage(controller.inputText.value); controller.sendMessage(controller.inputText.value);
textController.clear(); textController.clear();
}, },
),
)), )),
], ],
), ),
),
); );
} }
// 构建语音输入面板 // 构建语音输入面板
Widget _buildVoiceInputPanel() { Widget _buildVoiceInputPanel() {
final screenWidth = Get.width;
return Container( return Container(
width: screenWidth, decoration: BoxDecoration(
decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFFFFFFFF), // 纯白色开始 Colors.white,
Color(0xFFE8D5F5), // 更深的紫色结束 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: SafeArea(
child: SizedBox( child: SizedBox(
height: 100.h, height: 80.h,
child: Center(
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w), padding: EdgeInsets.symmetric(horizontal: 30.w),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// 左侧麦克风按钮 // 语音录制按钮
_buildMicrophoneButton(), 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( Expanded(
@ -333,75 +608,76 @@ class _ChatViewState extends State<ChatView> with TickerProviderStateMixin {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// 音频波形 // 音频波形区域
_buildAudioWaveform(), Container(
SizedBox(height: 10.h), height: 50.h,
// 状态文本 child: Obx(() {
Obx(() => Text( final isSpeaking = controller.isSpeechDetected.value;
controller.isSpeechDetected.value ? '正在聆听...' : '你可以开始说话', return Center(
style: TextStyle( child: Container(
fontSize: 16.sp, width: 220.w,
color: Colors.grey.shade700, height: 30.h,
fontWeight: FontWeight.w500, 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(
_buildCloseButton(), 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),
// 麦克风按钮 // 语音状态文本
Widget _buildMicrophoneButton() { Obx(() => Text(
return Container( controller.isSpeechDetected.value ? '正在聆听...' : '你可以开始说话',
width: 60.w, style: TextStyle(
height: 60.w, fontSize: 14.sp,
decoration: BoxDecoration( color: Colors.grey.shade700,
color: Colors.white, fontWeight: FontWeight.w500,
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() { Container(
return Container( width: 60.r,
width: 60.w, height: 60.r,
height: 60.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withOpacity(0.1),
blurRadius: 10, blurRadius: 8,
spreadRadius: 1, spreadRadius: 1,
), ),
], ],
@ -411,84 +687,17 @@ class _ChatViewState extends State<ChatView> with TickerProviderStateMixin {
icon: Icon( icon: Icon(
Icons.close, Icons.close,
color: Colors.grey.shade600, color: Colors.grey.shade600,
size: 28.sp, size: 28.r,
), ),
onPressed: controller.stopVoiceRecognition,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
onPressed: controller.stopVoiceRecognition,
), ),
), ),
);
}
// 音频波形
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),
), ),
), ),
), ),
);
},
), ),
); );
} }

71
lib/modules/explore/controllers/explore_controller.dart

@ -1,5 +1,7 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../data/providers/agent_provider.dart';
import '../../../core/routes/app_routes.dart';
class AIFeature { class AIFeature {
final String title; final String title;
@ -9,6 +11,8 @@ class AIFeature {
final Color endColor; final Color endColor;
final Color iconBgColor; final Color iconBgColor;
final Color iconColor; final Color iconColor;
final String agentId;
final String targetRoute;
AIFeature({ AIFeature({
required this.title, required this.title,
@ -18,6 +22,8 @@ class AIFeature {
required this.endColor, required this.endColor,
required this.iconBgColor, required this.iconBgColor,
required this.iconColor, required this.iconColor,
required this.agentId,
required this.targetRoute,
}); });
} }
@ -46,13 +52,26 @@ class ExploreController extends GetxController {
// AI功能列表 // AI功能列表
final List<AIFeature> aiFeatures = [ final List<AIFeature> aiFeatures = [
AIFeature( AIFeature(
title: '同声翻译', title: '全能助理',
description: '实时翻译40+种语言', description: '帮你解决各类问题',
icon: Icons.translate, icon: Icons.assistant,
startColor: Colors.blue.shade100, startColor: Colors.blue.shade100,
endColor: Colors.blue.shade200, endColor: Colors.blue.shade200,
iconBgColor: Colors.blue.withOpacity(0.2), iconBgColor: Colors.blue.withOpacity(0.2),
iconColor: Colors.blue.shade600, 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( AIFeature(
title: 'AI女友', title: 'AI女友',
@ -62,24 +81,19 @@ class ExploreController extends GetxController {
endColor: Colors.pink.shade200, endColor: Colors.pink.shade200,
iconBgColor: Colors.pink.withOpacity(0.2), iconBgColor: Colors.pink.withOpacity(0.2),
iconColor: Colors.pink.shade600, iconColor: Colors.pink.shade600,
agentId: 'cyber_girlfriend',
targetRoute: Routes.chat,
), ),
AIFeature( AIFeature(
title: '音乐创作', title: '健康顾问',
description: '用AI创作专属音乐', description: '提供科学健康建议',
icon: Icons.music_note, icon: Icons.healing,
startColor: Colors.green.shade100,
endColor: Colors.green.shade200,
iconBgColor: Colors.green.withOpacity(0.2),
iconColor: Colors.green.shade600,
),
AIFeature(
title: '声音克隆',
description: '创建你的数字声音',
icon: Icons.mic,
startColor: Colors.amber.shade100, startColor: Colors.amber.shade100,
endColor: Colors.amber.shade200, endColor: Colors.amber.shade200,
iconBgColor: Colors.amber.withOpacity(0.2), iconBgColor: Colors.amber.withOpacity(0.2),
iconColor: Colors.amber.shade600, iconColor: Colors.amber.shade600,
agentId: 'health_advisor',
targetRoute: Routes.chat,
), ),
]; ];
@ -129,13 +143,34 @@ class ExploreController extends GetxController {
void onAIFeatureTap(int index) { void onAIFeatureTap(int index) {
if (index < 0 || index >= aiFeatures.length) return; if (index < 0 || index >= aiFeatures.length) return;
final feature = aiFeatures[index];
final agent = AgentProvider.getAgentById(feature.agentId);
if (agent == null) {
Get.snackbar( Get.snackbar(
'功能', '错误',
'您选择了: ${aiFeatures[index].title}', '未找到对应的AI助手',
snackPosition: SnackPosition.TOP, 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);
}
} }
// 点击功能场景 // 点击功能场景

351
lib/modules/home/controllers/home_controller.dart

@ -4,7 +4,7 @@ import '../../../data/services/bluetooth_service.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart' hide BluetoothService; import 'package:flutter_blue_plus/flutter_blue_plus.dart' hide BluetoothService;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../data/services/voice_interaction_service.dart';
// 连接状态枚举 // 连接状态枚举
enum ConnectionStatus { enum ConnectionStatus {
disconnected, // 未连接 disconnected, // 未连接
@ -50,17 +50,70 @@ class HomeController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 检查是否有传入的蓝牙设备信息
// 立即加载缓存的设备信息并显示
_loadCachedDeviceInfo();
// 先进行简单初始化,避免立即进行蓝牙操作
_initBasicServices();
// 延迟初始化蓝牙相关功能,让UI先渲染完成
Future.delayed(const Duration(milliseconds: 1200), () {
_initBluetoothService();
});
}
// 立即加载缓存的设备信息
void _loadCachedDeviceInfo() {
try {
// 1. 首先检查是否有传入的设备信息(从PairingController传入)
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) { if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
Logger.info('使用传入的设备信息');
_deviceInfo.assignAll(Get.arguments); _deviceInfo.assignAll(Get.arguments);
_updateConnectionStatus(); _updateConnectionStatus();
update(); // 立即更新UI
return;
} }
// 获取蓝牙服务 // 2. 如果没有传入信息,尝试从BluetoothService获取上次连接的设备信息
_initBluetoothService(); if (Get.isRegistered<BluetoothService>()) {
BluetoothService bluetoothService = Get.find<BluetoothService>();
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<String, dynamic>) {
_deviceInfo.assignAll(Get.arguments);
_updateConnectionStatus();
}
// 模拟电池状态更新(不涉及设备通信)
_simulateBatteryUpdates(); _simulateBatteryUpdates();
} catch (e) {
Logger.error('初始化基础服务失败: $e');
}
} }
// 初始化蓝牙服务 // 初始化蓝牙服务
@ -69,52 +122,220 @@ class HomeController extends GetxController {
if (Get.isRegistered<BluetoothService>()) { if (Get.isRegistered<BluetoothService>()) {
_bluetoothService = Get.find<BluetoothService>(); _bluetoothService = Get.find<BluetoothService>();
// 如果已经有连接的设备但没有设备信息 // 不再重复加载设备信息,因为已经在_loadCachedDeviceInfo中加载过了
if (_deviceInfo.isEmpty && _bluetoothService!.selectedDevice != null) { // 只进行状态同步
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_updateConnectionStatus(); // 主动检查当前连接状态并更新
} else if (_deviceInfo.isEmpty) { _checkAndUpdateCurrentConnectionState();
// 如果没有设备信息,尝试获取已配对设备
_checkPairedDevices();
}
// 监听连接状态变化 // 监听连接状态变化
ever(_bluetoothService!.connectionStateObs, (state) { ever(_bluetoothService!.connectionStateObs, (state) {
Logger.info('蓝牙连接状态变化: $state');
_processConnectionStateChange(state); _processConnectionStateChange(state);
}); });
// 监听选中设备变化 // 监听选中设备变化
ever(_bluetoothService!.selectedDeviceObs, (device) { ever(_bluetoothService!.selectedDeviceObs, (device) {
Logger.info('蓝牙选中设备变化: ${device?.platformName ?? "无设备"}');
if (device != null) { if (device != null) {
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_updateConnectionStatus(); _updateConnectionStatus();
// 通知UI刷新
update();
} else if (_isConnected.value) { } else if (_isConnected.value) {
// 如果设备断开连接,更新状态 // 如果设备断开连接,更新状态
_isConnected.value = false; _isConnected.value = false;
_deviceInfo.clear(); // 不清除设备信息,以便可以显示上次连接的设备
_connectionStatus.value = ConnectionStatus.disconnected; _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) { } catch (e) {
Logger.error('获取蓝牙服务失败: $e'); Logger.error('获取蓝牙服务失败: $e');
_connectionStatus.value = ConnectionStatus.failed;
_connectionErrorMsg.value = '获取蓝牙服务失败'; _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<void> _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<BluetoothDevice> pairedDevices = await _bluetoothService!.getPairedDevices()
.timeout(const Duration(seconds: 5), onTimeout: () {
Logger.error('获取已配对设备超时');
return <BluetoothDevice>[];
});
// 记录配对设备
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) { void _processConnectionStateChange(BluetoothConnectionState state) {
// 强制从蓝牙服务获取最新设备信息
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
switch (state) { switch (state) {
case BluetoothConnectionState.connected: case BluetoothConnectionState.connected:
_connectionStatus.value = ConnectionStatus.connected; _connectionStatus.value = ConnectionStatus.connected;
_connectionErrorMsg.value = ''; _connectionErrorMsg.value = '';
_showConnectionSuccessMessage(); _isConnected.value = true; // 直接设置连接状态为true
// _showConnectionSuccessMessage();
break; break;
case BluetoothConnectionState.disconnected: case BluetoothConnectionState.disconnected:
_connectionStatus.value = ConnectionStatus.disconnected; _connectionStatus.value = ConnectionStatus.disconnected;
_isConnected.value = false; // 直接设置连接状态为false
break; break;
case BluetoothConnectionState.connecting: case BluetoothConnectionState.connecting:
_connectionStatus.value = ConnectionStatus.connecting; _connectionStatus.value = ConnectionStatus.connecting;
@ -124,7 +345,11 @@ class HomeController extends GetxController {
break; break;
} }
_updateConnectionStatus(); // 延迟一帧后刷新状态,确保UI能更新
Future.microtask(() => _updateConnectionStatus());
// 通知UI刷新
update();
} }
// 显示连接成功消息 // 显示连接成功消息
@ -147,70 +372,81 @@ class HomeController extends GetxController {
Future<void> _checkPairedDevices() async { Future<void> _checkPairedDevices() async {
try { try {
if (_bluetoothService != null) { if (_bluetoothService != null) {
Logger.info('开始检查已配对设备流程');
// 更新状态为正在连接 // 更新状态为正在连接
_connectionStatus.value = ConnectionStatus.connecting; _connectionStatus.value = ConnectionStatus.connecting;
update(); // 立即更新UI
// 如果已有选中的设备并且已连接,直接使用 // 如果已有选中的设备并且已连接,直接使用
if (_bluetoothService!.selectedDevice != null && if (_bluetoothService!.selectedDevice != null &&
_bluetoothService!.connectionState == BluetoothConnectionState.connected) { _bluetoothService!.connectionState == BluetoothConnectionState.connected) {
Logger.info('使用已连接的设备: ${_bluetoothService!.selectedDevice!.platformName}'); Logger.info('使用已连接的设备: ${_bluetoothService!.selectedDevice!.platformName}, 连接状态: ${_bluetoothService!.connectionState}');
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_connectionStatus.value = ConnectionStatus.connected; _connectionStatus.value = ConnectionStatus.connected;
_isConnected.value = true; // 直接设置连接状态
_updateConnectionStatus(); _updateConnectionStatus();
update(); // 立即更新UI
return; return;
} }
// 获取已配对设备 // 获取已配对设备
final pairedDevices = await _bluetoothService!.getPairedDevices(); Logger.info('开始获取系统已配对设备列表...');
final pairedDevices = await _bluetoothService!.getPairedDevices()
.timeout(const Duration(seconds: 5), onTimeout: () {
Logger.error('获取已配对设备超时');
return <BluetoothDevice>[];
});
if (pairedDevices.isEmpty) { if (pairedDevices.isEmpty) {
Logger.info('没有找到已配对设备'); Logger.info('没有找到已配对设备');
_connectionStatus.value = ConnectionStatus.disconnected; _connectionStatus.value = ConnectionStatus.disconnected;
update(); // 立即更新UI
return; return;
} }
// 遍历配对设备,查找已连接的设备 Logger.info('发现 ${pairedDevices.length} 个已配对设备');
for (var device in pairedDevices) { for (var i = 0; i < pairedDevices.length; i++) {
try { var device = pairedDevices[i];
final state = await device.connectionState.first; Logger.info('已配对设备 #$i: ${device.platformName} (${device.remoteId.str})');
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('开始查找设备进行连接...');
if (pairedDevices.isNotEmpty) { 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) { if (success) {
Logger.info('自动连接到已配对设备: ${pairedDevices.first.platformName}'); Logger.info('成功连接到设备: ${deviceToConnect.platformName ?? "未命名设备"}');
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_connectionStatus.value = ConnectionStatus.connected; _connectionStatus.value = ConnectionStatus.connected;
_isConnected.value = true;
_updateConnectionStatus(); _updateConnectionStatus();
update(); // 立即更新UI
} else { } else {
// 连接失败 // 连接失败
Logger.error('连接设备 ${deviceToConnect.platformName ?? "未命名设备"} 失败');
_connectionStatus.value = ConnectionStatus.failed; _connectionStatus.value = ConnectionStatus.failed;
_connectionErrorMsg.value = '连接设备失败,请重试'; _connectionErrorMsg.value = '连接设备失败,请重试';
update(); // 立即更新UI
} }
} else { } else {
_connectionStatus.value = ConnectionStatus.disconnected; _connectionStatus.value = ConnectionStatus.disconnected;
update(); // 立即更新UI
} }
} }
} catch (e) { } catch (e) {
Logger.error('检查已配对设备失败: $e'); Logger.error('检查已配对设备失败: $e');
_connectionStatus.value = ConnectionStatus.failed; _connectionStatus.value = ConnectionStatus.failed;
_connectionErrorMsg.value = '检查设备失败: ${e.toString()}'; _connectionErrorMsg.value = '检查设备失败: ${e.toString()}';
update(); // 立即更新UI
} }
} }
@ -369,13 +605,18 @@ class HomeController extends GetxController {
return; return;
} }
// 设置正在刷新状态 // 设置正在刷新状态,但保留当前设备信息显示
_connectionStatus.value = ConnectionStatus.connecting; _connectionStatus.value = ConnectionStatus.connecting;
update(); // 立即更新UI状态
// 强制检查当前蓝牙连接状态
_checkAndUpdateCurrentConnectionState();
if (_bluetoothService!.selectedDevice != null) { if (_bluetoothService!.selectedDevice != null) {
// 如果有选中的设备,刷新设备信息 // 如果有选中的设备,刷新设备信息
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_updateConnectionStatus(); _updateConnectionStatus();
update(); // 确保UI更新
// 检查刷新后的连接状态 // 检查刷新后的连接状态
if (_isConnected.value) { if (_isConnected.value) {
@ -387,19 +628,22 @@ class HomeController extends GetxController {
colorText: Colors.green[800], colorText: Colors.green[800],
); );
} else { } else {
// 设备可能已断开连接 // 设备可能已断开连接,尝试使用存储的设备信息重新连接
if (_bluetoothService!.lastConnectedDeviceInfo.isNotEmpty) {
_connectionStatus.value = ConnectionStatus.disconnected; _connectionStatus.value = ConnectionStatus.disconnected;
Get.snackbar( update(); // 更新状态
'提示', _reconnectToLastDevice();
'设备已断开连接', } else {
snackPosition: SnackPosition.TOP, // 上次连接的设备信息也为空,尝试连接已配对设备
backgroundColor: Colors.amber[100],
colorText: Colors.amber[800],
);
// 尝试重新连接
await _checkPairedDevices(); await _checkPairedDevices();
} }
}
} else if (_bluetoothService!.lastConnectedDeviceInfo.isNotEmpty) {
// 如果没有选中的设备但有上次连接的设备信息,尝试重新连接
_deviceInfo.assignAll(_bluetoothService!.lastConnectedDeviceInfo);
_updateConnectionStatus();
update(); // 确保UI更新
_reconnectToLastDevice();
} else { } else {
// 如果没有选中的设备,尝试连接已配对设备 // 如果没有选中的设备,尝试连接已配对设备
await _checkPairedDevices(); await _checkPairedDevices();
@ -426,6 +670,7 @@ class HomeController extends GetxController {
Logger.error('刷新设备信息失败: $e'); Logger.error('刷新设备信息失败: $e');
_connectionStatus.value = ConnectionStatus.failed; _connectionStatus.value = ConnectionStatus.failed;
_connectionErrorMsg.value = '刷新设备信息失败: ${e.toString()}'; _connectionErrorMsg.value = '刷新设备信息失败: ${e.toString()}';
update(); // 确保状态更新
Get.snackbar( Get.snackbar(
'提示', '提示',

156
lib/modules/pairing/controllers/pairing_controller.dart

@ -49,9 +49,9 @@ class PairingController extends GetxController {
// 启动搜索动画 // 启动搜索动画
_startSearchingAnimation(); _startSearchingAnimation();
// 延迟初始化蓝牙服务,避免在应用启动时就请求权限 // 延迟更久初始化蓝牙服务,避免在应用启动时就请求权限和阻塞主线程
// 改为延迟初始化,给UI足够时间渲染 // 改为延迟初始化,给UI足够时间渲染
Future.delayed(const Duration(milliseconds: 500), () { Future.delayed(const Duration(milliseconds: 1500), () {
_initBluetoothService(); _initBluetoothService();
}); });
} }
@ -69,6 +69,7 @@ class PairingController extends GetxController {
if (Get.isRegistered<BluetoothService>()) { if (Get.isRegistered<BluetoothService>()) {
_bluetoothService = Get.find<BluetoothService>(); _bluetoothService = Get.find<BluetoothService>();
} else { } else {
// 在后台初始化蓝牙服务
_bluetoothService = await Get.put(BluetoothService()).init(); _bluetoothService = await Get.put(BluetoothService()).init();
} }
@ -82,8 +83,8 @@ class PairingController extends GetxController {
// 如果蓝牙开启,开始扫描设备 // 如果蓝牙开启,开始扫描设备
if (on) { if (on) {
// 延迟开始扫描,避免权限请求过早 // 延迟开始扫描,避免权限请求过早和主线程阻塞
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 800), () {
_startScanningDevices(); _startScanningDevices();
}); });
} else { } else {
@ -96,8 +97,8 @@ class PairingController extends GetxController {
// 如果蓝牙已经开启,直接开始扫描 // 如果蓝牙已经开启,直接开始扫描
if (_bluetoothService.isOn) { if (_bluetoothService.isOn) {
_isBluetoothOn.value = true; _isBluetoothOn.value = true;
// 延迟开始扫描,避免权限请求过早 // 延迟开始扫描,避免权限请求过早和主线程阻塞
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 800), () {
_startScanningDevices(); _startScanningDevices();
}); });
} else { } else {
@ -155,116 +156,66 @@ class PairingController extends GetxController {
// 开始扫描蓝牙设备 // 开始扫描蓝牙设备
Future<void> _startScanningDevices() async { Future<void> _startScanningDevices() async {
try { try {
// 先检查是否已有连接的设备 _searchStatus.value = SearchStatus.searching;
if (_bluetoothService.connectedDevices.isNotEmpty) {
// 有已连接的设备,直接使用
_deviceFound(_bluetoothService.connectedDevices.first);
return;
}
// 检查是否有已配对的设备 // 先检查是否有上次连接的设备信息
if (await _checkPairedDevices()) { if (_bluetoothService.lastConnectedDeviceInfo.isNotEmpty &&
// 已找到并成功连接到配对设备 _bluetoothService.lastConnectedDeviceInfo['connected'] == true) {
Logger.info('使用上次连接的设备信息');
_deviceInfo.value = _bluetoothService.lastConnectedDeviceInfo;
_onDeviceFound();
return; return;
} }
// 搜索新设备 // 使用BluetoothService的自动连接功能查找设备
_searchStatus.value = SearchStatus.searching; Logger.info('尝试自动连接设备');
await _bluetoothService.startScan(timeout: const Duration(seconds: 10)); BluetoothDevice? device = await _bluetoothService.autoConnectDevice()
.timeout(const Duration(seconds: 8), onTimeout: () {
Logger.info('自动连接设备超时');
return null;
});
// 检查扫描结果 if (device != null) {
if (_bluetoothService.scanResults.isNotEmpty) { Logger.info('自动连接设备成功: ${device.platformName}');
// 找到设备,选择信号最强的一个 _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo();
final strongestSignal = _bluetoothService.scanResults _onDeviceFound();
.reduce((a, b) => (a.rssi > b.rssi) ? a : b); return;
}
// 连接设备 // 如果自动连接失败,尝试扫描新设备
final success = await _bluetoothService.connectToDevice(strongestSignal.device); Logger.info('自动连接失败,开始扫描新设备');
device = await _bluetoothService.scanAndConnectToDevice(
timeout: const Duration(seconds: 5)
).timeout(const Duration(seconds: 6), onTimeout: () {
Logger.info('扫描并连接设备超时');
return null;
});
if (success) { if (device != null) {
_deviceFound(strongestSignal.device); Logger.info('扫描并连接设备成功: ${device.platformName}');
} else { _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo();
_errorMessage.value = '连接设备失败,请重试'; _onDeviceFound();
_searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation();
}
} else { } else {
// 未找到设备 // 未找到任何设备
_errorMessage.value = '未找到蓝牙设备,请确保设备已开启并在附近'; _errorMessage.value = '未找到蓝牙设备,请确保设备已开启并在附近';
_searchStatus.value = SearchStatus.failed; _searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation(); _stopSearchingAnimation();
} }
} catch (e) { } catch (e) {
Logger.error('扫描蓝牙设备失败: $e'); Logger.error('设备搜索和连接失败: $e');
_errorMessage.value = '扫描蓝牙设备失败: ${e.toString()}'; _errorMessage.value = '设备搜索失败: ${e.toString()}';
_searchStatus.value = SearchStatus.failed; _searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation(); _stopSearchingAnimation();
} }
} }
// 检查已配对设备 // 设备找到并连接成功后的处理
Future<bool> _checkPairedDevices() async { void _onDeviceFound() {
try {
// 先检查当前是否有设备已连接
if (_bluetoothService.selectedDevice != null &&
_bluetoothService.connectionState == BluetoothConnectionState.connected) {
Logger.info('已有设备连接: ${_bluetoothService.selectedDevice!.platformName}');
_deviceFound(_bluetoothService.selectedDevice!);
return true;
}
List<BluetoothDevice> 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) {
_searchStatus.value = SearchStatus.found; _searchStatus.value = SearchStatus.found;
_stopSearchingAnimation(); _stopSearchingAnimation();
// 获取设备信息 // 更新日志并延迟跳转到主页
_deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); Logger.info('设备连接成功,准备跳转到主页');
// 延迟后跳转到主页,同时传递设备信息
Timer(const Duration(seconds: 1), () { Timer(const Duration(seconds: 1), () {
Get.offAllNamed(Routes.home, arguments: _deviceInfo.value); Get.offAllNamed(Routes.home, arguments: _deviceInfo.value);
}); });
@ -272,24 +223,25 @@ class PairingController extends GetxController {
// 手动连接 // 手动连接
void manualConnect() { void manualConnect() {
if (_searchStatus.value == SearchStatus.searching) { // 停止当前扫描(如果有)
// 如果正在搜索,则停止搜索
_bluetoothService.stopScan(); _bluetoothService.stopScan();
}
// 重新开始搜索 // 更新UI状态
_startSearchingAnimation(); _startSearchingAnimation();
_searchStatus.value = SearchStatus.searching; _searchStatus.value = SearchStatus.searching;
_errorMessage.value = '';
// 直接开始扫描并连接
_startScanningDevices(); _startScanningDevices();
} }
// 稍后再说 // 稍后再说
void skipConnecting() { void skipConnecting() {
if (_searchStatus.value == SearchStatus.searching) { // 停止当前扫描
// 如果正在搜索,则停止搜索
_bluetoothService.stopScan(); _bluetoothService.stopScan();
} _stopSearchingAnimation();
// 直接跳转到主页
Get.offAllNamed(Routes.home); Get.offAllNamed(Routes.home);
} }
} }
Loading…
Cancel
Save