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
相关产品推荐
相关产品推荐

