海狸IM技术升级:从Uniapp到Flutter的跨平台重构之路

做IM应用这几年,最开始用Uniapp写移动端,随着持续的迭代,一些技术瓶颈逐渐凸显。从Uniapp到Flutter的重构,不是心血来潮,而是深思熟虑后的选择。

为什么要换

性能瓶颈

IM应用的核心是消息,消息量大的时候渲染性能就成了瓶颈。在Uniapp里,消息列表一旦超过几百条,往上滑就开始卡顿,特别是有图片和表情的时候。用户截图发过来,加载速度也不太理想,经常出现图片加载中、消息发送中这些状态长时间不消失的情况。

Flutter的渲染机制不同。它直接调用Skia引擎绘制UI,不存在WebView或者原生控件的桥接损耗。实际跑起来,消息列表滑动的流畅度明显上一个台阶,即使是2000条消息的列表,滑动起来依然顺滑。

音视频功能缺失

原本的版本里一直没有音视频通话功能,这是用户反馈最多的需求之一。但Uniapp做实时音视频的方案,要么依赖原生插件,要么用WebRTC的Web实现,两种方案我都试过:

  • 原生插件:调试麻烦,打包流程复杂,而且跨平台维护成本高,Android和iOS需要分别处理不同的插件版本
  • WebRTC Web方案:性能损耗明显,通话质量不理想,特别是在网络不好的情况下

Flutter这边有成熟的LiveKit客户端 livekit_client,直接对接LiveKit服务,音视频通话的实现反而比想象中顺利。从发起通话到接通,整个流程的体验和原生应用差不多。

拍照与多媒体采集

本地存储局限

Uniapp的本地存储方案,核心是key-value结构,比如uni.setStorage。对于IM这种需要频繁查询、结构复杂的数据,不太够用。消息多了之后,查询历史消息、按会话分组等操作都变得很慢。

Flutter这边我用了Drift(原名 Moor),它是SQLite的ORM方案,可以写类型安全的SQL查询。本地消息缓存、联系人列表、群组信息都可以做本地持久化,离线也能看历史消息,联网后自动同步增量数据。

数据库状态与同步管理

跨平台维护成本

Uniapp写一次跑多端听起来美好,但实际开发中总会遇到各端兼容性问题,需要写不少条件编译。比如微信小程序的API限制、H5的浏览器差异、原生App的权限处理,这些都需要单独处理。

Flutter虽然也有平台通道,但整体一致性更好,一套代码改动的调试成本低很多。特别是在UI层面,Flutter的跨平台表现几乎一致,不用为不同平台做太多适配。

技术方案的选择

Flutter版本

我用的是Flutter 3.x + Dart 3.x。这个组合的 null safety 和新语法特性让代码质量更有保障,减少了运行时错误。

依赖这块,核心的几个:

  • flutter_bloc: ^9.1.1 # 状态管理
  • go_router: ^17.1.0 # 路由
  • drift: ^2.32.0 # SQLite ORM
  • livekit_client: ^2.6.5 # 音视频通话
  • dio: ^5.9.2 # HTTP客户端
  • web_socket_channel: ^3.0.3 # WebSocket

项目结构

参考了官方推荐的项目结构,按功能模块组织:

lib/
├── core/                     # 核心基础设施
│   ├── business/            # 业务逻辑实现
│   ├── database/            # 本地数据库
│   ├── datasync/            # 数据同步
│   └── message/             # 消息处理
├── features/                # 业务模块
│   ├── auth/                # 登录注册
│   ├── chat/                # 聊天
│   ├── friend/              # 好友
│   ├── group/               # 群组
│   └── call/                # 音视频通话
├── shared/                  # 共享组件
└── di/                      # 依赖注入

分层清晰的好处是改需求的时候影响范围可控,不会牵一发而动全身。

我的群聊列表

本地数据库设计

消息表是最核心的一张表,和PC端保持一致。字段设计包括消息ID、会话ID、消息类型、发送状态等,支持离线重试和消息排序。

音视频通话实现

这块是全新加的功能。Flutter端通过LiveKit实现,后端需要搭建LiveKit服务。核心流程:

  1. 用户发起通话 → 调用后端API创建room
  2. 获取token和room信息
  3. Flutter端连接LiveKit房间
  4. 订阅远端用户的音视频轨道

重写过程中的坑

状态管理选型

一开始纠结用Provider还是BLoC,最后选了BLoC。它的单向数据流在复杂业务场景下更好维护,测试也方便。不过Bloc的代码量确实多些,这是取舍。

WebSocket重连

IM应用里WebSocket断线重连是刚需。Flutter端实现了心跳机制和自动重连,消息发送有ACK确认,超时重试。这块逻辑参考了商业IM的设计,确保消息的可靠传递。

多端数据同步

Flutter端和已有的PC端共用同一个后端API,数据格式必须一致。同步策略是增量拉取+本地优先,减少网络请求,提升用户体验。

通讯录好友列表

性能优化

Flutter虽然性能好,但也需要优化:

  • 消息列表用ListView.builder,避免一次性渲染所有消息
  • 图片用CachedNetworkImage,减少重复请求
  • 状态管理用BLoC,避免不必要的重建
  • 数据库查询加索引,提升查询速度

收藏表情

重构前后对比

性能对比

场景 Uniapp Flutter
消息列表滑动 卡顿(500条消息) 流畅(2000条消息)
图片加载 较慢,经常显示占位符 快速,本地缓存命中率高
启动速度 3-5秒 2-3秒
音视频通话 不支持 支持,质量稳定

功能对比

功能 Uniapp Flutter
文本消息 支持 支持
图片消息 支持 支持
表情消息 支持 支持
音视频通话 不支持 支持
离线消息 有限支持 完整支持
本地缓存 key-value存储 SQLite数据库

发起群聊界面

现在的状态

重构后的Flutter版本已经接替了原来的Uniapp版本,成为海狸IM的移动端主力。音视频通话、本地消息缓存、表情收藏这些之前难做的功能,现在都跑起来了。

消息主界面

当然,Flutter不是银弹。它的包体积比H5大,首页启动速度在低端机上不如原生。这些都是后续优化方向。

重构经验总结

  1. 技术选型要匹配需求:如果你的应用需要高性能、复杂功能,Flutter是个好选择;如果只是轻量应用,Uniapp可能更合适。

  2. 重写不是坏事:有时候重构比在旧代码上打补丁更高效,特别是当技术栈已经成为瓶颈时。

  3. 分层设计很重要:清晰的项目结构让后续维护和扩展更轻松。

  4. 性能优化不能忽视:即使是Flutter,也需要合理的优化才能达到最佳效果。

  5. 用户体验是核心:技术最终是为用户服务的,所有的技术选型都应该以提升用户体验为目标。

聊天消息详情界面

发出的好友申请历史

相关链接

项目源码:

  • 后端源码:https://github.com/wsrh8888/beaver-server
  • 移动端Flutter源码:https://github.com/wsrh8888/beaver-flutter
  • 移动端旧版源码:https://github.com/wsrh8888/beaver-unaipp
  • PC端源码:https://github.com/wsrh8888/beaver-desktop.git
  • 后台管理系统源码:https://github.com/wsrh8888/beaver-manager

学习资源:

  • 在线文档:https://wsrh8888.github.io/beaver-docs/

核心教学视频:

  • 本地搭建教程合集:https://space.bilibili.com/269553626/lists/6075764?type=season
  • 服务器部署教程合集:https://space.bilibili.com/269553626/lists/6075828?type=season
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐