Fix critical bugs in speech recognition system
1. Add finalResult property to SpeechRecognitionResult class - Distinguish between real-time and final recognition results - Update factory methods, toString, equals, and hashCode - Update toMap and fromMap methods 2. Update YxAsrService to support finalResult flag - Add finalResult parameter to _sendResult method - Mark final results with finalResult: true - Keep real-time results as finalResult: false (default) 3. Remove unused methods to clean up codebase - Remove unused _toggleRecording method - Remove unused _updateTextController method - Clean up orphaned comments These fixes resolve linter errors and ensure proper text appending functionality.
This commit is contained in:
parent
ea32370fcc
commit
d63124203b
|
|
@ -160,72 +160,6 @@ class _SpeechRecognitionPageState extends State<SpeechRecognitionPage> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 更新文本控制器内容
|
|
||||||
void _updateTextController() {
|
|
||||||
// 组合已确认的结果和当前实时结果
|
|
||||||
final confirmedText = _realtimeResults.join(' ');
|
|
||||||
final displayText = _currentText.isNotEmpty
|
|
||||||
? '$confirmedText ${_currentText}'.trim()
|
|
||||||
: confirmedText;
|
|
||||||
|
|
||||||
// 只有当内容真正改变时才更新,避免光标跳动
|
|
||||||
if (_textController.text != displayText) {
|
|
||||||
final cursorPosition = _textController.selection.baseOffset;
|
|
||||||
_textController.text = displayText;
|
|
||||||
|
|
||||||
// 尝试保持光标位置,如果超出范围则移到末尾
|
|
||||||
if (cursorPosition <= displayText.length) {
|
|
||||||
_textController.selection = TextSelection.fromPosition(
|
|
||||||
TextPosition(offset: cursorPosition),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_textController.selection = TextSelection.fromPosition(
|
|
||||||
TextPosition(offset: displayText.length),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 切换录音状态
|
|
||||||
Future<void> _toggleRecording() async {
|
|
||||||
if (!_isInitialized) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (_isListening) {
|
|
||||||
print('📱 [Example] 停止录音');
|
|
||||||
await _speechService.stopListening();
|
|
||||||
|
|
||||||
// 录音结束后,将当前识别的文本保存到历史记录
|
|
||||||
if (_currentText.isNotEmpty) {
|
|
||||||
setState(() {
|
|
||||||
_realtimeResults.add(_currentText);
|
|
||||||
_recognitionHistory.insert(0, _currentText);
|
|
||||||
print('📱 [Example] 添加到历史记录: $_currentText');
|
|
||||||
// 保持历史记录在合理数量
|
|
||||||
if (_recognitionHistory.length > 10) {
|
|
||||||
_recognitionHistory.removeLast();
|
|
||||||
}
|
|
||||||
// 清空当前文本
|
|
||||||
_currentText = '';
|
|
||||||
_updateTextController();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print('📱 [Example] 开始录音');
|
|
||||||
// 清空之前的结果,开始新的录音
|
|
||||||
setState(() {
|
|
||||||
_realtimeResults.clear();
|
|
||||||
_currentText = '';
|
|
||||||
_textController.clear();
|
|
||||||
});
|
|
||||||
await _speechService.startListening(partialResults: true);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print('📱 [Example] 录音操作失败: $e');
|
|
||||||
_showErrorSnackBar('录音操作失败: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,14 @@ class SpeechRecognitionResult {
|
||||||
/// 备选识别结果
|
/// 备选识别结果
|
||||||
final List<String> alternatives;
|
final List<String> alternatives;
|
||||||
|
|
||||||
|
/// 是否为最终结果(true: 最终结果, false: 实时结果)
|
||||||
|
final bool finalResult;
|
||||||
|
|
||||||
const SpeechRecognitionResult({
|
const SpeechRecognitionResult({
|
||||||
required this.recognizedWords,
|
required this.recognizedWords,
|
||||||
this.confidence = 0.0,
|
this.confidence = 0.0,
|
||||||
this.alternatives = const [],
|
this.alternatives = const [],
|
||||||
|
this.finalResult = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// 从 Map 创建 [SpeechRecognitionResult] 实例
|
/// 从 Map 创建 [SpeechRecognitionResult] 实例
|
||||||
|
|
@ -21,6 +25,7 @@ class SpeechRecognitionResult {
|
||||||
recognizedWords: map['recognizedWords'] as String? ?? '',
|
recognizedWords: map['recognizedWords'] as String? ?? '',
|
||||||
confidence: (map['confidence'] as num?)?.toDouble() ?? 0.0,
|
confidence: (map['confidence'] as num?)?.toDouble() ?? 0.0,
|
||||||
alternatives: List<String>.from(map['alternatives'] as List? ?? []),
|
alternatives: List<String>.from(map['alternatives'] as List? ?? []),
|
||||||
|
finalResult: map['finalResult'] as bool? ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,13 +35,14 @@ class SpeechRecognitionResult {
|
||||||
'recognizedWords': recognizedWords,
|
'recognizedWords': recognizedWords,
|
||||||
'confidence': confidence,
|
'confidence': confidence,
|
||||||
'alternatives': alternatives,
|
'alternatives': alternatives,
|
||||||
|
'finalResult': finalResult,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SpeechRecognitionResult(recognizedWords: $recognizedWords, '
|
return 'SpeechRecognitionResult(recognizedWords: $recognizedWords, '
|
||||||
'confidence: $confidence, alternatives: $alternatives)';
|
'confidence: $confidence, alternatives: $alternatives, finalResult: $finalResult)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -45,6 +51,7 @@ class SpeechRecognitionResult {
|
||||||
return other is SpeechRecognitionResult &&
|
return other is SpeechRecognitionResult &&
|
||||||
other.recognizedWords == recognizedWords &&
|
other.recognizedWords == recognizedWords &&
|
||||||
other.confidence == confidence &&
|
other.confidence == confidence &&
|
||||||
|
other.finalResult == finalResult &&
|
||||||
other.alternatives.length == alternatives.length &&
|
other.alternatives.length == alternatives.length &&
|
||||||
other.alternatives.every((alt) => alternatives.contains(alt));
|
other.alternatives.every((alt) => alternatives.contains(alt));
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +60,7 @@ class SpeechRecognitionResult {
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return recognizedWords.hashCode ^
|
return recognizedWords.hashCode ^
|
||||||
confidence.hashCode ^
|
confidence.hashCode ^
|
||||||
|
finalResult.hashCode ^
|
||||||
alternatives.hashCode;
|
alternatives.hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -586,6 +586,7 @@ class YxAsrService implements SpeechRecognitionService {
|
||||||
recognizedWords: result.text,
|
recognizedWords: result.text,
|
||||||
confidence: 1.0,
|
confidence: 1.0,
|
||||||
alternatives: [],
|
alternatives: [],
|
||||||
|
finalResult: true, // 标记为最终结果
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debugPrint('⚠️ [YxAsr] 流式最终结果为空');
|
debugPrint('⚠️ [YxAsr] 流式最终结果为空');
|
||||||
|
|
@ -785,12 +786,14 @@ class YxAsrService implements SpeechRecognitionService {
|
||||||
required String recognizedWords,
|
required String recognizedWords,
|
||||||
required double confidence,
|
required double confidence,
|
||||||
required List<String> alternatives,
|
required List<String> alternatives,
|
||||||
|
bool finalResult = false,
|
||||||
}) {
|
}) {
|
||||||
debugPrint('📤 [YxAsr] 发送识别结果: "$recognizedWords"');
|
debugPrint('📤 [YxAsr] 发送识别结果: "$recognizedWords"');
|
||||||
final result = SpeechRecognitionResult(
|
final result = SpeechRecognitionResult(
|
||||||
recognizedWords: recognizedWords,
|
recognizedWords: recognizedWords,
|
||||||
confidence: confidence,
|
confidence: confidence,
|
||||||
alternatives: alternatives,
|
alternatives: alternatives,
|
||||||
|
finalResult: finalResult,
|
||||||
);
|
);
|
||||||
_resultController.add(result);
|
_resultController.add(result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue