海狸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服务。核心流程:
- 用户发起通话 → 调用后端API创建room
- 获取token和room信息
- Flutter端连接LiveKit房间
- 订阅远端用户的音视频轨道
重写过程中的坑
状态管理选型
一开始纠结用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大,首页启动速度在低端机上不如原生。这些都是后续优化方向。
重构经验总结
-
技术选型要匹配需求:如果你的应用需要高性能、复杂功能,Flutter是个好选择;如果只是轻量应用,Uniapp可能更合适。
-
重写不是坏事:有时候重构比在旧代码上打补丁更高效,特别是当技术栈已经成为瓶颈时。
-
分层设计很重要:清晰的项目结构让后续维护和扩展更轻松。
-
性能优化不能忽视:即使是Flutter,也需要合理的优化才能达到最佳效果。
-
用户体验是核心:技术最终是为用户服务的,所有的技术选型都应该以提升用户体验为目标。


相关链接
项目源码:
- 后端源码: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
学习资源:
核心教学视频: