Flutter WebSocket场景下如何先执行异步TTS再按顺序展示事件消息
解决方案
你现在的实现方式之所以不能按顺序执行,核心原因是ListView.builder会同时初始化可视区域内的所有列表项,每个项对应的FutureBuilder都会立刻触发_speak异步方法,不存在先后等待的逻辑,自然无法实现读完一条再处理下一条的需求。
实现思路
不要直接将所有收到的消息丢给渲染层处理,拆分两个队列做逻辑隔离:
- 缓冲队列:接收WebSocket推送的所有消息,暂存待处理
- 渲染队列:只存已经完成朗读、可以展示给用户的消息
新增串行消费逻辑:每次仅从缓冲队列取1条消息,等待朗读完成后再加入渲染队列刷新UI,之后再处理下一条消息,完全避免并发执行。你需要的就是这种队列消费的方案,不要在ListView.builder里嵌套FutureBuilder做异步处理,不符合列表的渲染逻辑。
代码实现
第一步:定义状态变量
// 待处理消息缓冲队列 final List<MessageModel> _pendingMessages = []; // 已完成朗读、可展示的消息列表 final List<MessageModel> _renderedMessages = []; // 标记当前是否正在处理消息,避免并发消费 bool _isProcessing = false;
第二步:修改WebSocket消息接收逻辑
socket.on('messageToClient', (data) { print("socket $data"); MessageModel message = MessageModel.fromJson(data); // 收到消息先加入缓冲队列 _pendingMessages.add(message); // 触发队列消费逻辑 _processMessageQueue(); });
第三步:实现串行队列消费逻辑
Future<void> _processMessageQueue() async { // 正在处理中 或 缓冲队列为空时直接返回 if (_isProcessing || _pendingMessages.isEmpty) return; _isProcessing = true; try { // 取出队首第一条消息 final currentMessage = _pendingMessages.removeAt(0); // 等待当前消息朗读完成 await _speak(currentMessage.message); // 朗读完成后加入渲染队列,刷新UI setState(() { _renderedMessages.add(currentMessage); }); } catch (e) { print("消息处理出错:$e"); } finally { _isProcessing = false; // 递归调用处理下一条消息 _processMessageQueue(); } }
第四步:修改ListView渲染逻辑
ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, itemCount: _renderedMessages.length, itemBuilder: (BuildContext context, int indexMessage) { return TextMessage( message: _renderedMessages[indexMessage].message, author: _renderedMessages[indexMessage].author, hint: _renderedMessages[indexMessage].hint, options: _renderedMessages[indexMessage].options, giveVoice: _renderedMessages[indexMessage].giveVoice, ); }, )
注意事项
- 请确保
_speak方法会等待朗读完全完成后再返回Future,部分场景下flutter_tts默认的speak方法返回时机可能不符合预期,可以用Completer搭配朗读完成回调包装,确保异步等待逻辑准确,示例参考:Future<void> _speak(String text) async { final Completer completer = Completer(); FlutterTts flutterTts = FlutterTts(); flutterTts.setCompletionHandler(() { completer.complete(); }); await flutterTts.speak(text); return completer.future; } - 页面销毁时记得清空缓冲队列、关闭socket、停止tts朗读,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Julien d'Adhemar
相关产品推荐
相关产品推荐

