我正在使用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;
}
但这种方法导致了红屏错误:
════════手势捕捉到异常═══════════════════════════════════════════以下断言在处理手势时引发:setState()回调参数返回了一个Future。
_HomePageState#0f8a上的setState()方法是用返回Future的闭包或方法。也许它被标记为"异步";。
不是在对setState()的调用中执行异步工作,首先执行工作(不更新小部件状态),然后在对setState()的调用中同步更新状态。
当抛出异常时,这是堆栈#0 State.setState.backage:flutter/…/widgets/framework.dart:1270#1 State.setState包:flutter/…/widgets/framework。dart:1286#2 _主页状态.build.lib\page\home_page.dart:75#3手势识别器.invokeCallback软件包:flutter/…/手势/识别器.dart:182#4长按手势识别器_checkLongPressStart软件包:flutter/…/sportions/long_press.dart:423…处理程序:"onLongPress";识别器:LongPressGestureRecognizer#ac97bdebugOwner:手势检测器状态:可能════════════════════════════════════════════════════════════════════════════════I/flutter(11164):正在侦听
════════小部件库捕获到异常═══════════════════════════════════方法"toLowerCase"为对null调用。接收器:null尝试调用:toLowerCase()导致相关错误的小部件是SubstringHighlightlib\page\home_page.dart:45════════════════════════════════════════════════════════════════════════════════
,所以我想我可以使用resultListener
,它只在有voicerecognition
结果时被调用,将lastwords
字符串发送到主Widget中的文本变量,但我还没有弄清楚如何。
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):重复:123 I/chatty(20460uid=10166(com.example.speech_to_ext_example)1.ui相同1行2I/flutter(20460):重复:123 I/chatty(20460uid=10166(com.example.speech_to_text_example)1.ui相同的1行I/flutter(20460):recibido:123
对于这个特定的例子,这并不重要,但如果我想将该字符串用于消费Web服务之类的事情,这是一个问题,我只想在每次开始录制时获得一次。
谢谢。
如果您想同时显示lastwords,我认为您需要在lastwords更改时通知父类。将您的语音识别类移动到窗口小部件可以使技巧。在顶部创建一个变量,而不是lastwords,并显示在文本小部件中。
void startListening() async {
await _speech.listen(
onResult: (val) =>
setState(() {
_lastwords=val.recognizedWords;
});
cancelOnError: true,
partialResults: false);
}
我的方法是在将future分配给setstate 中的文本之前,使用一个额外的变量来存储future
onLongPress: () async {
var txt = await Speech.startListening();
setState(() {
text = txt;
});
}
我以前的解决方案的问题是,我使用StreamController作为广播,每次使用麦克风按钮时,侦听器都会创建一个实例,从而导致冗余数据。为了解决这种情况,我在Speech类中创建了一个流控制器,然后在结果监听器中添加了一个接收器。
StreamController<String> streamController = StreamController();
static resultListener(SpeechRecognitionResult result) {
lastWords = "${result.recognizedWords}";
//print(lastWords);
if (lastWords != '') {
streamController.sink.add(lastWords);
}
在主Widget中,我在initState中调用了streamController侦听器,因此侦听器只实例化一次;流已经被收听"错误。
@override
void initState() {
super.initState();
initSpeechState();
speech.streamController.stream.listen((event) {
setState(() {
text = event;
print(text);
});
});
}