You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

476 lines
19 KiB

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<FlutterAsrTestController> {
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<String>(
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<String>(
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;
}
}
}