当前位置: 首页 > 知识库问答 >
问题:

flatter将数据从类传递到小部件变量的最佳方式

谢奕
2023-03-14

我正在使用Speech_to_文本包将语音识别结果存储在一个字符串变量中,以后可以用于不同的目的,到目前为止,我只想在屏幕上显示该字符串。我想实现与Whatsap录制类似的功能,因此我使用onLongPress启动录制和onLongPress停止录制。

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分别调用属于不同类的方法。

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);
    }
  }
}

我认为我只需要将starListning方法分配给主小部件中的Text变量

 onLongPress: () {
              setState(() async {
                text = await Speech.startListening();
              });

给定a方法html" target="_blank">返回所需的字符串

 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状态。设定状态。包:flatter/../widgets/framework。dart:1270#1州。setState包:flatter/../widgets/framework。dart:1286#2#主页状态。建筑库\页\主页。dart:75#3手势识别器。invokeCallback包:颤振/../signatures/recognizer。dart:182#4长按手势识别器_选中长按启动程序包:颤振/手势/长按。飞镖:423。。。处理程序:“onLongPress”识别器:LongPressGestureRecognizer#ac97b调试所有者:GestureDetector状态:可能════════════════════════════════════════════════════════════════════════════════ I/flatter(11164):倾听

════════ widgets库捕获到异常═══════════════════════════════════ 对null调用了方法“toLowerCase”。Receiver:null尝试调用:toLowerCase(),相关的错误导致小部件是SubstringHighlight lib\page\home\u page。省道:45════════════════════════════════════════════════════════════════════════════════

,所以我想我可以使用resultListener,它只有在出现voicerecognition结果时才会被调用,以将lastwords字符串发送到主小部件中的文本变量,但我还没有弄清楚如何发送。

P.D.在主窗口小部件上使用所有方法都是可行的,但我正在尝试实现干净的体系结构,所以我希望将该逻辑与Ui窗口小部件分开。

谢谢

//--------更新-----------------

我一直在研究,并找到了一种使用流的方法,

我在Speech类中创建了一个流控制器,然后在结果侦听器中添加了一个接收器。

static resultListener(SpeechRecognitionResult result) {
    lastWords = "${result.recognizedWords}";
    //print(lastWords);
    if (lastWords != '') {
      streamController.sink.add(lastWords);
    }

在主窗口小部件中,我在OnLongPress函数中实现了流侦听。

  onLongPress: () {
              Speech.startListening();
              Speech.streamController.stream.listen((data) {
                print("recibido: $data");
                setState(() {
                  text = data;
                });
              });

到目前为止,它运行良好,但我不知道这是否是处理数据传输的最佳方式。如果有人知道更好的方法,我将不胜感激,如果这是一个好方法,这将是这个问题的答案。

// .-............... 第二次更新--------------------

这种方法似乎有效,但我意识到,过了一段时间,我开始多次获得相同的数据

I/flatter(20460):监听I/flatter(20460):非监听I/flatter(20460):recibido:123 I/chatty(20460):uid=10166(com.example.speech\u to\u text\u示例)1。ui相同1第2行I/flatter(20460):recibido:123 I/chatty(20460):uid=10166(com.example.speech\u to\u text\u示例)1。ui相同1线I/颤振(20460):recibido:123

对于这个特殊的例子来说,这并不重要,但是如果我想使用这个字符串来做一些事情,比如消费一个网络服务,这是一个问题,我只想在每次开始录制时得到它一次。

谢谢

共有3个答案

邓业
2023-03-14

我以前的解决方案的问题是,我使用StreamController作为广播,每次我使用麦克风按钮时,监听器都会创建一个实例,导致冗余数据。为了解决这种情况,我在Speech类中创建了一个流控制器,然后在结果监听器中添加了一个接收器。

StreamController<String> streamController = StreamController();

static resultListener(SpeechRecognitionResult result) {
    lastWords = "${result.recognizedWords}";
    //print(lastWords);
    if (lastWords != '') {
      streamController.sink.add(lastWords);
}

在主窗口小部件中,我在initState中调用了streamController侦听器,因此侦听器只实例化了一次,这防止了冗余数据和“流已经被侦听”错误。

@override
  void initState() {
    super.initState();
    initSpeechState();    
    speech.streamController.stream.listen((event) {
      setState(() {
        text = event;
        print(text);
      });
 
    });
  }  
苏墨竹
2023-03-14

我的方法是在将future分配给setstate中的文本之前,使用一个额外的变量来存储future

onLongPress: () async  {
   var txt = await Speech.startListening();
   setState(() {
      text = txt;
   });
}
赫连瑾瑜
2023-03-14

我认为如果您想同时显示lastwords,您需要在lastwords更改时通知父类。将语音识别类移动到widget可以实现这一技巧。在顶部创建一个变量,而不是lastwords,并显示在文本小部件中。

 void startListening() async {
    await _speech.listen(
        onResult: (val) =>
    setState(() {
   _lastwords=val.recognizedWords;
      });
        cancelOnError: true,
        partialResults: false);
   
  }
 类似资料:
  • 我是Flutter和Dart的新手。我有一些来自基于api json的数据,数据的变量称为。我从官方flutter留档中获取了这个示例代码,我希望能够使用变量并替换字符串文本,如下所示: 但是,我在第行标题中遇到了一个错误:const Text(data[index][“name]),,错误是类型常量创建的参数必须是常量表达式。此错误来自Android Studio本身(版本3.2) 但当我使用这

  • 问题内容: 目前,我在隐藏的输入字段中回显某些变量,并在需要时使用Javascript读取它们。 我和一个同事现在正在考虑使用PHP生成一个额外的Javascript文件,该文件仅包含Javascript的所有变量。这样,变量已经存在,HTML中没有多余的代码。 有什么好的方法可以将变量从PHP传递到Javascript?我们的解决方案听起来如何? 问题答案: 通用数据传递 JavaScript常

  • 问题内容: 我在PHP中有一个变量,我的JavaScript代码中需要它的值。如何将变量从PHP转换为JavaScript? 我有看起来像这样的代码: 我有需要和看起来类似以下内容的JavaScript代码: 问题答案: 实际上,有几种方法可以做到这一点。有些需要比其他更多的开销,有些被认为比其他更好。 没有特别的顺序: 使用AJAX从服务器获取所需的数据。 将数据回显到页面中的某个位置,然后使用

  • 问题内容: 我想根据例如从jenkins传递的变量来运行测试用例,请选择要运行的测试用例:testcaseOne,testcaseTwo 在pom.xml(maven)中: 我有两个testng @test方法: 还有我的testng.xml文件: 如何根据我要运行的测试数量和数量来传递此参数?也许有完全不同的方式来做到这一点? 问题答案: 您可以在testng.xml文件http://testn

  • 问题内容: 我看过类似的论坛,但无法获得任何有效的解决方案。我正在尝试将变量从Flask传递到我的JavaScript文件。这些值将用于我的JavaScript文件中的PubNub。 这是我的Python代码的一部分: 这是我的JavaScript代码(app.js)的一部分: 如果我在Settings.html文件中使用了此代码,而在app.js文件中却没有使用,则此代码有效。 问题答案: 该m

  • 问题内容: 我正在使用Google Maps制作可以加载数据库中包含经纬度数据的标记的地图。我希望用户可以通过单击按钮来加载三个不同的“图层”。单击该按钮时,将在服务器上执行php函数,从而根据数据库中的信息创建xml文件。然后调用AJAX函数提取此xml数据,然后将其用于创建地图标记。没有为每个“层”使用单独的PHP函数(除了SQL查询所在的行,这是相同的东西),有没有办法将AJAX中的java