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

Flutter+Laravel开发聊天应用如何实现无加载圈实时收发消息

聊天系统卡顿问题优化方案

问题核心诱因

  • 现有方案基于HTTP请求-响应模型做消息收发,搭配定时轮询拉取消息,天生存在延迟高、无效请求多、服务端压力大的缺陷,不可能达到即时通讯的流畅度要求
  • 每次收发消息都触发全局isLoading状态更新,插入LinearProgressIndicator会导致整个聊天页面重绘、布局跳变,是体验卡顿的直接原因
  • 轮询机制无论怎么调整间隔,都无法平衡“消息延迟”和“资源消耗”的矛盾,轮询间隔短会产生大量空请求浪费带宽,间隔长则消息延迟明显。

落地方案(实现类Messenger的流畅聊天体验)

1. 通信层替换为WebSocket长连接

  • Laravel后端直接使用官方内置的Laravel Broadcasting能力,搭配开源轻量WebSocket服务Soketi实现长连接能力,无需从零手写WebSocket服务,部署维护成本极低。
  • Flutter客户端使用web_socket_channel包对接WebSocket服务,建立连接后全程保持长连状态:服务端产生新消息时会主动推送到客户端,消息延迟可控制在100ms以内,彻底替代轮询方案,没有无效空转请求。
  • 消息发送逻辑拆分:文本、表情等轻量消息直接通过WebSocket通道发送;图片、文件等大体积资源先走HTTP分片上传拿到资源访问地址,再将携带资源地址的消息体通过WebSocket发送,不需要等待接口全量返回后再更新UI。

2. 状态层改造为乐观UI模式,移除全局加载逻辑

  • 彻底删除日常收发消息时触发isLoading、展示全局LinearProgressIndicator的逻辑:全局加载指示器仅在首次进入聊天页面拉取第一页历史消息时展示一次,后续收发消息全程不弹出全局加载组件打断用户操作。
  • 发送消息实现乐观更新:用户点击发送按钮后,立刻生成带临时唯一ID、标记为“发送中”状态的消息插入本地消息列表,GetX仅触发消息列表的局部更新,不刷新整个页面;等WebSocket收到服务端返回的消息发送成功回执后,再将对应临时ID的消息状态更新为发送成功,如果触发发送失败则标记为失败状态支持点击重发,用户全程感知不到等待。
  • 消息接收实现增量更新:WebSocket收到新消息时,先校验消息ID是否已经存在于本地消息列表,不存在才插入到列表对应位置,仅触发单条消息的组件重绘,不做全量列表刷新。

3. 体验细节补全

  • 给WebSocket连接增加30s间隔的心跳保活机制,断网后自动重试连接,重连成功后自动拉取断线期间漏收的消息,保证消息不丢、顺序不乱。
  • 消息列表使用ListView.builder做懒加载渲染,历史消息分页拉取,加载更早的历史消息时仅在列表顶部展示小尺寸加载提示,不触发全局页面加载状态。
  • 对消息项、输入框、发送按钮做独立的响应式组件封装,GetX更新状态时仅刷新对应组件,把重绘范围压缩到最小,避免不必要的UI开销。

核心实现代码片段

Flutter侧WebSocket初始化与消息监听

// 初始化WebSocket连接
final wsChannel = IOWebSocketChannel.connect(
  'wss://你的服务域名/ws/binding',
  pingInterval: const Duration(seconds: 30),
);

// 监听服务端主动推送的新消息
wsChannel.stream.listen((rawData) {
  final msgData = jsonDecode(rawData);
  final newMsg = MessageModel.fromJson(msgData);
  // 增量插入消息,不触发全局loading
  Get.find<MessageController>().addMessageIfNotExists(newMsg);
});

发送消息乐观更新逻辑

Future<void> sendTextMessage(String content) async {
  // 生成本地临时消息
  final tempMessage = MessageModel(
    tempId: const Uuid().v4(),
    content: content,
    sendStatus: MessageSendStatus.sending,
    createTime: DateTime.now(),
  );
  // 先把本地消息插入列表,用户立刻看到自己发的内容
  Get.find<MessageController>().addLocalMessage(tempMessage);
  // 通过WebSocket发送消息,无需等待返回
  wsChannel.sink.add(jsonEncode(tempMessage.toSendPayload()));
}

Laravel侧消息推送逻辑

// 消息入库完成后,向对话参与方广播新消息事件
broadcast(new NewMessageBroadcast($message))->toOthers();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:06:19