import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../controllers/speech_test_controller.dart'; import '../../../data/services/speech_factory.dart'; class SpeechTestView extends GetView { const SpeechTestView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('语音测试'), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: () => controller.switchServiceType(controller.selectedServiceType.value), tooltip: '重新初始化', ), ], ), body: Obx(() { return controller.isInitialized.value ? _buildTestContent() : _buildLoadingView(); }), ); } Widget _buildLoadingView() { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('初始化语音服务中...'), ], ), ); } Widget _buildTestContent() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildServiceTypeSelector(), const SizedBox(height: 24), _buildTtsSection(), const SizedBox(height: 24), const Divider(), const SizedBox(height: 24), _buildAsrSection(), ], ), ); } Widget _buildServiceTypeSelector() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '选择语音服务', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Obx(() { return Wrap( spacing: 8, children: [ for (final type in SpeechServiceType.values) ChoiceChip( label: Text(_getServiceTypeName(type)), selected: controller.selectedServiceType.value == type, onSelected: (selected) { if (selected) { controller.switchServiceType(type); } }, ), ], ); }), ], ), ), ); } String _getServiceTypeName(SpeechServiceType type) { switch (type) { case SpeechServiceType.azure: return 'Azure语音'; case SpeechServiceType.iflytek: return '讯飞语音'; case SpeechServiceType.google: return 'Google语音'; case SpeechServiceType.alibaba: return '阿里语音'; case SpeechServiceType.volcano: return '豆包语音'; case SpeechServiceType.flutter: return 'Flutter本地'; } } Widget _buildTtsSection() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '语音合成测试', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), TextField( controller: controller.textController, decoration: const InputDecoration( labelText: '输入要合成的文本', border: OutlineInputBorder(), ), maxLines: 3, ), const SizedBox(height: 16), Obx(() { return Text( '状态: ${controller.ttsStatus}', style: TextStyle( color: _getStatusColor(controller.ttsStatus.value), fontWeight: FontWeight.bold, ), ); }), const SizedBox(height: 16), Wrap( spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, children: [ Obx(() { return ElevatedButton.icon( icon: const Icon(Icons.play_arrow), label: const Text('单次播放'), onPressed: controller.isProcessing.value || controller.isSpeaking.value ? null : controller.speakOnce, ); }), Obx(() { return ElevatedButton.icon( icon: const Icon(Icons.playlist_play), label: const Text('连续播放测试'), onPressed: controller.isProcessing.value || controller.isSpeaking.value ? null : controller.speakContinuously, ); }), Obx(() { return ElevatedButton.icon( icon: const Icon(Icons.stop), label: const Text('停止'), onPressed: controller.isSpeaking.value ? controller.stopSpeaking : null, ); }), ], ), const SizedBox(height: 8), const Divider(), const SizedBox(height: 8), const Text( '连续播放测试会依次播放以下句子:', style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), for (int i = 0; i < controller.testSentences.length; i++) Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Text('${i + 1}. ${controller.testSentences[i]}'), ), ], ), ), ); } Widget _buildAsrSection() { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '语音识别测试', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Obx(() { return Text( '状态: ${controller.recognitionStatus}', style: TextStyle( color: _getStatusColor(controller.recognitionStatus.value), fontWeight: FontWeight.bold, ), ); }), const SizedBox(height: 8), Obx(() { final hasLanguage = controller.detectedLanguage.value.isNotEmpty; return Visibility( visible: hasLanguage, child: Padding( padding: const EdgeInsets.only(bottom: 8), child: Text( '检测语言: ${controller.detectedLanguage.value}', style: const TextStyle( fontStyle: FontStyle.italic, color: Colors.blue, ), ), ), ); }), const SizedBox(height: 8), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(8), ), constraints: const BoxConstraints(minHeight: 100), width: double.infinity, child: Obx(() { final displayText = controller.getDisplayResult(); return RichText( text: TextSpan( style: const TextStyle(fontSize: 16, color: Colors.black), children: [ if (displayText.isEmpty) const TextSpan( text: '识别结果将显示在这里', style: TextStyle(color: Colors.grey, fontStyle: FontStyle.italic), ) else if (displayText.contains('正在识别:')) TextSpan( text: displayText.split('正在识别:')[0], children: [ const TextSpan( text: '\n\n正在识别: ', style: TextStyle( color: Colors.blue, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, ), ), TextSpan( text: displayText.split('正在识别:')[1].trim(), style: const TextStyle( color: Colors.blue, fontStyle: FontStyle.italic, ), ), ], ) else TextSpan(text: displayText), ], ), ); }), ), const SizedBox(height: 16), Wrap( spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, children: [ Obx(() { return ElevatedButton.icon( icon: const Icon(Icons.mic), label: const Text('一次性识别'), onPressed: controller.isProcessing.value || controller.isListening.value || controller.isSpeaking.value ? null : controller.recognizeOnce, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, ), ); }), Obx(() { return ElevatedButton.icon( icon: Icon( controller.isListening.value ? Icons.mic_off : Icons.mic_none, ), label: Text( controller.isListening.value ? '停止连续识别' : '开始连续识别', ), onPressed: controller.isProcessing.value || controller.isSpeaking.value ? null : controller.isListening.value ? controller.stopContinuousRecognition : controller.startContinuousRecognition, style: ElevatedButton.styleFrom( backgroundColor: controller.isListening.value ? Colors.red : Colors.blue, ), ); }), ], ), ], ), ), ); } Color _getStatusColor(String status) { if (status.contains('错误') || status.contains('失败')) { return Colors.red; } else if (status.contains('完成') || status.contains('就绪')) { return Colors.green; } else if (status.contains('正在')) { return Colors.blue; } else { return Colors.black; } } }