You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 23:15:05