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

Flutter Bloc聊天发消息后其他用户无法实时接收新消息

问题根因

你当前的实现只有主动拉取逻辑,没有服务端消息变更的被动监听能力:用户A发消息时,仅A端触发了AddMessage事件、主动调用接口拉取了最新消息列表,B端完全感知不到会话内的新消息,自然不会触发UI刷新。Twilio Conversations本身提供实时事件推送能力,你目前仅使用REST API做主动请求,无法被动接收其他用户触发的消息更新。

分步改造方案

1. 修复现有代码基础Bug

你当前ChatState的copyWith方法未做参数赋值,属于无效实现,先修正:

ChatState copyWith({
  Messages? messages,
}) {
  return ChatState(
    messages: messages ?? this.messages,
  );
}

2. 新增消息监听相关事件

在chat_event.dart中新增两个事件,分别用于启动实时监听、收到新消息时触发状态更新:

// 启动实时消息监听
class StartMessageListen extends ChatEvent {}

// 收到新消息时触发UI更新
class NewMessageReceived extends ChatEvent {
  final dynamic newMessage; // 替换为你项目中实际的单条消息类型
  const NewMessageReceived(this.newMessage);

  @override
  List<Object> get props => [newMessage];
}

3. 改造消息服务,接入实时消息流

不要仅依赖REST接口,接入Twilio官方客户端SDK后,绑定会话的消息新增监听,通过广播流对外推送新消息事件:

注意:不推荐用定时轮询REST接口的方案实现实时更新,该方案延迟高、会产生大量无效请求,浪费带宽和服务端资源

// 消息服务内新增广播流控制器
final StreamController messageStreamController = StreamController.broadcast();

// 初始化会话监听
Future<void> initConversationListener() async {
  // 完成Twilio客户端身份校验、获取当前会话实例
  // 绑定会话的消息新增监听事件
  conversation.onMessageAdded.listen((newMessage) {
    // 新消息到达时,将消息加入广播流
    messageStreamController.add(newMessage);
  });
}

4. 改造ChatBloc逻辑

Bloc初始化时启动消息流订阅,收到新消息时自动更新状态,同时记得在Bloc销毁时取消订阅防止内存泄漏:

class ChatBloc extends Bloc<ChatEvent, ChatState> {
  StreamSubscription? _messageSub;

  ChatBloc() : super(ChatState.initial()) {
    // 启动实时监听
    on<StartMessageListen>((event, emit) async {
      await HttpService().initConversationListener();
      // 订阅消息流
      _messageSub = HttpService().messageStreamController.stream.listen((newMsg) {
        add(NewMessageReceived(newMsg));
      });
    });

    // 初始化加载历史消息
    on<InitialChatEvent>((event, emit) async {
      final chatFeed = await HttpService().getMessages();
      emit(ChatState(messages: chatFeed));
    });
    
    // 发送消息
    on<AddMessage>((event, emit) async {
      await HttpService().postMessage(event.messageToPost);
      // 发送后无需主动拉取全量列表,新消息会通过onMessageAdded事件自动触发更新,避免重复请求
    });

    // 收到新消息时更新状态
    on<NewMessageReceived>((event, emit) async {
      // 将新消息追加到现有列表,无需全量拉取
      final updatedMsgList = List.from(state.messages.messages)..add(event.newMessage);
      emit(state.copyWith(messages: Messages(messages: updatedMsgList)));
    });
  }

  @override
  Future<void> close() {
    // 销毁时取消订阅
    _messageSub?.cancel();
    HttpService().messageStreamController.close();
    return super.close();
  }
}

5. 页面侧触发监听

在聊天页面初始化时同时触发监听启动和历史消息加载事件:

// 在ChatPage的initState生命周期中添加
@override
void initState() {
  super.initState();
  context.read<ChatBloc>().add(StartMessageListen());
  context.read<ChatBloc>().add(InitialChatEvent());
}
补充说明
  • 如果你暂时无法接入Twilio客户端SDK,可临时用定时轮询方案:每隔1-2秒调用一次getMessages接口拉取最新列表,但该方案仅适合临时调试,不可用于生产环境
  • 你之前发送消息后全量拉取列表的逻辑,仅对当前操作用户生效,其他端因为没有触发对应事件,完全不会执行拉取动作,这是多端无法同步的核心原因
  • 离开聊天页面时记得释放Twilio客户端监听资源,避免后台常驻造成内存泄漏和不必要的流量消耗

内容的提问来源于stack exchange,提问作者Marcel Dz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:42:17