【发布时间】:2021-08-11 02:06:39
【问题描述】:
我正在使用 Speech_to_text 包 将语音识别结果存储在一个字符串变量中,以后我可以将其用于不同的目的,到目前为止,我只想在屏幕上显示字符串。我想实现类似于 Whatsaap 录制的功能,所以我有 GestureDetector 与 onLongPress 开始录制和 onLongPressUp 停止它。
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//Variable to show on screen
String text = 'Press the button and start speaking';
bool isListening = false;
final SpeechToText speech = SpeechToText();
String lastError = "";
String lastStatus = "";
String _currentLocaleId = "";
@override
void initState() {
super.initState();
initSpeechState();
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(MyApp.title),
centerTitle: true,
),
body: SingleChildScrollView(
reverse: true,
padding: const EdgeInsets.all(30).copyWith(bottom: 150),
child: SubstringHighlight(
text: text,
terms: Command.all,
textStyle: TextStyle(
fontSize: 32.0,
color: Colors.black,
fontWeight: FontWeight.w400,
),
textStyleHighlight: TextStyle(
fontSize: 32.0,
color: Colors.red,
fontWeight: FontWeight.w400,
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: AvatarGlow(
animate: isListening,
endRadius: 75,
glowColor: Theme.of(context).primaryColor,
child: GestureDetector(
child: FloatingActionButton(
child: Icon(isListening ? Icons.mic : Icons.mic_none, size: 36),
/*onPressed: () {
toggleRecording();
print(text);
}*/
onPressed: () {},
),
onLongPress: () {
setState(() async {
text = await Speech.startListening();
});
// print(text);
},
onLongPressUp: Speech.stopListening,
//sendMessage(),
),
),
);
Future<void> initSpeechState() async {
bool hasSpeech = await speech.initialize(
onError: errorListener, onStatus: statusListener, debugLogging: false);
}
//Speech to text methods
Future<void> errorListener(SpeechRecognitionError error) async {
print("Received error status: $error, listening: ${speech.isListening}");
if (mounted) {
setState(() {
lastError = "${error.errorMsg} - ${error.permanent}";
print(lastError);
});
}
}
void statusListener(String status) {
setState(() {
print(status);
lastStatus = "$status";
});
}
}
OnLongPress 和 OnLongPressUp 分别调用不同类中的方法 startListening 和 stopListening。
class Speech {
static final _speech = SpeechToText();
static String lastWords;
void cancelListening() {
_speech.cancel();
}
static Future startListening() async {
await _speech.listen(
onResult: resultListener,
listenFor: Duration(minutes: 1),
cancelOnError: true,
partialResults: false);
return lastWords;
}
static void stopListening() {
_speech.stop();
}
static resultListener(SpeechRecognitionResult result) {
lastWords = "${result.recognizedWords}";
//print(lastWords);
if (lastWords != '') {
//this is what I want to pass to the Text variable on the Main Widget
print(lastWords);
}
}
}
我以为我只需要将 startListening 方法分配给主小部件中的 Text 变量
onLongPress: () {
setState(() async {
text = await Speech.startListening();
});
鉴于athe 方法正在返回所需的字符串
static Future startListening() async {
await _speech.listen(
onResult: resultListener,
listenFor: Duration(minutes: 1),
cancelOnError: true,
partialResults: false);
return lastWords;
}
但这种方法导致了 redScreen 错误:
════════手势捕获的异常 ═════════════════════════════════════════╕下面的断言 在处理手势时被抛出:setState() 回调参数 返回一个 Future。
_HomePageState#0ff8a 上的 setState() 方法被调用 返回 Future 的闭包或方法。也许它被标记为 “异步”。
不是在 setState() 调用中执行异步工作,而是 首先执行工作(不更新小部件状态),然后 在对 setState() 的调用中同步更新状态。
抛出异常时,这是堆栈 #0 状态.setState。包:flutter/…/widgets/framework.dart:1270 #1 State.setState 包:flutter/…/widgets/framework.dart:1286 #2 _HomePageState.build。 lib\page\home_page.dart:75 #3 GestureRecognizer.invokeCallback 包:flutter/…/gestures/recognizer.dart:182 #4 LongPressGestureRecognizer._checkLongPressStart package:flutter/.../gestures/long_press.dart:423 ... 处理程序: “onLongPress”识别器:LongPressGestureRecognizer#ac97b debugOwner:手势检测器 状态:可能══════════════════════════════════════════════════════════ ═════════════════════════════════ I/flutter (11164):聆听
════════ 小部件库捕获的异常 ═══════════════════════════════════方法'toLowerCase'是 调用为空。接收者:null 尝试调用:toLowerCase() 相关的导致错误的小部件是 SubstringHighlight lib\page\home_page.dart:45 ══════════════════════════════════════════════════ ══════════════════════════════
,所以我想我可以使用resultListener,它只在有voicerecognition 结果时被调用,将lastwords 字符串发送到主窗口小部件中的文本变量,但我没有想通了。
P.D. 让主小部件上的所有方法都有效,但我试图实现干净的架构,所以我想将该逻辑与 Ui 小部件分开。
谢谢。
//-------- 更新 -----
我一直在研究,并找到了一种使用流使其工作的方法,
我在 Speech 类中创建了一个流控制器,然后在结果监听器中添加了一个接收器。
static resultListener(SpeechRecognitionResult result) {
lastWords = "${result.recognizedWords}";
//print(lastWords);
if (lastWords != '') {
streamController.sink.add(lastWords);
}
在主 Widget 中,我在 OnLongPress 函数中实现了流监听。
onLongPress: () {
Speech.startListening();
Speech.streamController.stream.listen((data) {
print("recibido: $data");
setState(() {
text = data;
});
});
到目前为止,它运行良好,但我不知道这是否是处理此数据传输的最佳方式。如果有人知道更好的方法,我将不胜感激,如果这是一个很好的方法,那就是这个问题的答案。
// .-................ 第二次更新--------
这种方法似乎有效,但我意识到一段时间后我开始多次获得相同的数据
I/flutter (20460): 听 I/flutter (20460): notListening I/flutter (20460):recibido:123 I/chatty (20460): uid=10166(com.example.speech_to_text_example) 1.ui 相同 1 行 2 I/flutter (20460):recibido:123 I/chatty (20460): uid=10166(com.example.speech_to_text_example) 1.ui 相同 1 行 I/flutter (20460):recibido:123
对于这个无关紧要的特定示例,但如果我想将该字符串用于诸如使用网络服务之类的问题,那么我只想在每次开始录制时获取一次。
谢谢。
【问题讨论】:
标签: flutter dart speech-to-text