import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../controllers/flutter_asr_test_controller.dart'; import '../../../data/services/flutter_asr_service.dart'; /// Flutter ASR测试视图,演示如何使用FlutterAsrService class FlutterAsrTestView extends GetView { const FlutterAsrTestView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter ASR Test'), centerTitle: true, ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 语音识别控制卡片 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Speech Recognition Controls', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), // 添加连续识别模式开关 Obx(() { final isContinuous = controller.continuousMode; return SwitchListTile( title: const Text('连续识别模式'), subtitle: Text( isContinuous ? '已开启:识别将自动连续进行' : '已关闭:识别完成后需手动重新开始', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), value: isContinuous, onChanged: controller.isAvailable ? (value) => controller.toggleContinuousMode() : null, activeColor: Colors.blue, dense: true, ); }), const Divider(height: 24), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Obx(() { final isListening = controller.isListening; return ElevatedButton.icon( onPressed: isListening || !controller.isAvailable ? null : () => controller.startListening(), icon: isListening ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.mic), label: Text(isListening ? 'Listening...' : 'Start Listening'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, ), ); }), Obx(() { final isActive = controller.isListening; return ElevatedButton.icon( onPressed: isActive ? () => controller.stopListening() : null, icon: const Icon(Icons.stop), label: const Text('Stop'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), ); }), ], ), ], ), ), ), const SizedBox(height: 16), // 语言选择卡片 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Language Selection', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Obx(() { final locales = controller.locales; final currentLocale = controller.currentLocale; if (locales.isEmpty) { return const Padding( padding: EdgeInsets.all(8.0), child: Text( 'No languages available. Please check ASR settings on your device.', style: TextStyle( color: Colors.red, fontStyle: FontStyle.italic, ), ), ); } return DropdownButtonFormField( decoration: const InputDecoration( labelText: 'Select Language', border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), ), value: currentLocale.isNotEmpty ? currentLocale : null, items: locales.map((locale) { final name = locale.name ?? 'Unknown'; final id = locale.localeId ?? ''; return DropdownMenuItem( value: id, child: Text('$name (${id.isNotEmpty ? id : 'Unknown'})'), ); }).toList(), onChanged: controller.isListening ? null : (value) { if (value != null) { controller.setLocale(value); } }, isExpanded: true, hint: const Text('Select a language'), ); }), ], ), ), ), const SizedBox(height: 16), // 当前识别结果卡片 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Current Recognition', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Obx(() { final text = controller.recognizedText; final confidence = controller.confidence; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade300), ), width: double.infinity, child: Text( text.isEmpty ? 'Speak to see recognition results here' : text, style: TextStyle( fontSize: 16, fontStyle: text.isEmpty ? FontStyle.italic : FontStyle.normal, color: text.isEmpty ? Colors.grey : Colors.black, ), ), ), if (confidence > 0) Padding( padding: const EdgeInsets.only(top: 8.0), child: Text( 'Confidence: ${(confidence * 100).toStringAsFixed(1)}%', style: TextStyle( color: _getConfidenceColor(confidence), fontWeight: FontWeight.bold, ), ), ), ], ); }), ], ), ), ), const SizedBox(height: 16), // 历史记录卡片 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Recognition History', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), Obx(() { final hasHistory = controller.history.isNotEmpty; return TextButton.icon( onPressed: hasHistory ? () => controller.clearHistory() : null, icon: const Icon(Icons.clear_all, size: 18), label: const Text('Clear'), style: TextButton.styleFrom( foregroundColor: Colors.red, padding: const EdgeInsets.symmetric(horizontal: 8), ), ); }), ], ), const SizedBox(height: 8), Obx(() { final history = controller.history; if (history.isEmpty) { return const Padding( padding: EdgeInsets.symmetric(vertical: 16.0), child: Center( child: Text( 'No recognition history yet', style: TextStyle( fontStyle: FontStyle.italic, color: Colors.grey, ), ), ), ); } return ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: history.length, separatorBuilder: (context, index) => const Divider(height: 1), itemBuilder: (context, index) { final text = history[index]; return ListTile( title: Text(text), leading: CircleAvatar( backgroundColor: Colors.blue.shade100, radius: 18, child: Text( '${index + 1}', style: const TextStyle( color: Colors.blue, fontWeight: FontWeight.bold, ), ), ), dense: true, ); }, ); }), ], ), ), ), const SizedBox(height: 16), // 状态显示卡片 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Status', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Obx(() { final state = controller.asrState; final stateText = _getAsrStateText(state); final stateColor = _getAsrStateColor(state); final isContinuous = controller.continuousMode; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: stateColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: stateColor.withOpacity(0.3)), ), child: Row( children: [ Icon( _getAsrStateIcon(state), color: stateColor, ), const SizedBox(width: 8), Text( 'Current Status: $stateText', style: TextStyle( color: stateColor, fontWeight: FontWeight.bold, ), ), ], ), ), if (isContinuous) Padding( padding: const EdgeInsets.only(top: 8.0), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.blue.withOpacity(0.3)), ), child: Row( children: const [ Icon( Icons.repeat, color: Colors.blue, size: 16, ), SizedBox(width: 8), Text( '连续识别模式已开启', style: TextStyle( color: Colors.blue, fontWeight: FontWeight.bold, fontSize: 12, ), ), ], ), ), ), ], ); }), ], ), ), ), ], ), ), ), ); } // 获取ASR状态文本 String _getAsrStateText(AsrState state) { switch (state) { case AsrState.notInitialized: return 'Not Initialized'; case AsrState.initialized: return 'Initialized'; case AsrState.listening: return 'Listening'; case AsrState.notListening: return 'Not Listening'; case AsrState.error: return 'Error'; default: return 'Unknown'; } } // 获取ASR状态颜色 Color _getAsrStateColor(AsrState state) { switch (state) { case AsrState.notInitialized: return Colors.grey; case AsrState.initialized: return Colors.blue; case AsrState.listening: return Colors.green; case AsrState.notListening: return Colors.orange; case AsrState.error: return Colors.red; default: return Colors.grey; } } // 获取ASR状态图标 IconData _getAsrStateIcon(AsrState state) { switch (state) { case AsrState.notInitialized: return Icons.hourglass_empty; case AsrState.initialized: return Icons.check_circle; case AsrState.listening: return Icons.mic; case AsrState.notListening: return Icons.mic_off; case AsrState.error: return Icons.error; default: return Icons.help_outline; } } // 获取置信度颜色 Color _getConfidenceColor(double confidence) { if (confidence >= 0.8) { return Colors.green; } else if (confidence >= 0.6) { return Colors.orange; } else { return Colors.red; } } }