前言
不断写作,不断成长。
最近在使用Flutter开发了交流模块内容,在开发中不断出现很多小BUG以及不断维护功能,经过沉淀最后写下该文章以记录成长,避免重复踩坑
技术栈
因为开发的是应用,所以则使用Flutter进行页面涉及以及相关逻辑处理
使用WebSocket实现多人聊天
WebSocket结构
我所使用的WebSocket中的相关参数结构(仅供参考)
- user_id 发送者ID
- send_name 发送者名字
- receive_id确定接收者(多个以“,”分隔开,全部用all)
- message发送内容(不得超过100字)
WebSocket服务端中会对每次发包的receiver_id进行验证处理,必须为数据库中的uid
Flutter发包示例
var packet = {
"user_id" : userId,
"send_name": name,
"message": message,
"title": title,
"type": 2
};
//发包
socketHandle.sink.add(packet)
功能模块
重要的就以下俩个模块
最近聊天模块(Message)、聊天窗模块(Chat)
最近聊天模块
这里需要主要的事项有很多,因为会出现三种情况,注:(一定要切记这三种情况出现,我就是在这里出现问题)
为了方便介绍功能实现,这里简化A为用户A,B为用户B。
1、当A发送至消息给B B未发送消息给A过
2、当B未发送过给A消息 B只做接收者
3、当B发送消息和A A-B实现点对点聊天
切记,在这里实现最近聊天模块是一定要注意这三个点!!!
排序处理
后台有一张表为:ChatMessage 包含了几个重要参数
send_id、receiver_id、message、time
在获取完所有消息列表后,可以通过List的sort方法
示例
recordList.sort((a,b)=> DateTime.parse(b?.time ?? "").compareTo(DateTime.parse(a?.time ?? "")));
这样可以获取到消息的完整排序列表
转化为最近聊天对象
最近聊天实体类我是这样设计的
class RecordLately {
String? _portrait;
String? _title;
String? _message;
String? _time;
bool? _selected;
}
portrait 头像、title为最近聊天的标题、message消息,time时间用来排序
转化数据库ChatMessage为最近聊天实体类方法
@override
RecordLately createRecordLately(ChatMessage ChatMessage,[int? recordId]) {
String title = "";
String portrait = "";
String message = ChatMessage?.message ?? "";
//这里的type指的是联系人/群聊
int type = ChatMessage.groupId == null ? 0 : 1;
String time = ChatMessage?.time ?? "";
switch(type){
case 0:
int receiverId = recordId ?? 0;
User receiverUser = getUserById(receiverId);
title = receiverMeetingUser.username!;
//1男 2女
portrait = receiverUser.gender == 1 ? PortraitEnum.MAN.assets :PortraitEnum.WOMAN.assets;
break;
}
var json = {
"title": title,
"portrait": portrait,
"message": message,
"time": time,
"type": type,
"selected": false
};
RecordLately recordLately = RecordLately.fromJson(json);
return recordLately;
}
之后传递到Message中初始化赋值渲染即可!
聊天模块
这个地方重点来了,涉及到title和交流信息数据 最好是还需要设计一个RecordInfo实体类对象用来做消息记录
RecordInfo实体类如下
class RecordInfo {
String? _message;
int? _type;
String? _time;
int? _sort;
}
RecordInfo用来做ChatMessage数据库实体类的包装类
message最新消息、type区分是作为接收者还是发送者、time时间用来排序、sort用来当每条消息的记录排序
绑定消息记录
这里需要将ChatMessage进行全局化,意味着你可以放置在Common文件夹下定义Static类型
需要给予一个根据发送者id和接收者id的方法进行过滤消息列表
Flutter示例代码
/*
* 通过接收者id获取所有相关的会议聊天信息
*/
List<ChatMessage> selectMessageListByReceiverId(int sendId,int receiverId){
return state.ChatMessage.where((element){
return sendId == element.sendId && receiverId == element.receiverId;
}).toList();
}
拥有了这个,已经完成了Chat所需要具备的参数
定义 Map类型的recordMap key为title,value为List<RecordInfo>
转化ChatMessage为RecordInfo
示例方法
/*
* 转换聊天记录表为聊天数据实体对象
*/
List<RecordInfo> parseMessageToRecordInfo(List<ChatMessage> Message,{int type = -1}){
List<RecordInfo> recordInfo = [];
for(int i = 0;i<Message.length;i++){
var element = Message[i];
Map<String,dynamic> recordMap = {
"message": element.message,
//type 0> 自己 1别人
"type" : type == -1 ? element?.styleType : type,
"time": Utils.parseDateTimeByString(element?.time ?? "").toString(),
"sort": i
};
RecordInfo info = RecordInfo.fromJson(recordMap);
print('RecordInfo: message: ${info.message}, Time: ${info.time}, Sort: ${info.sort}');
recordInfo.add(info);
}
return recordInfo;
}
最后通过用户在Message页点击某个人,之后通过路由携带title参数进行跳转,再通过title解析相关ChatMessage即可!
发送消息
关于在Chat页中,发送消息时所需要注意的事项
- 插入接口数据
- 重载全局ChatMessage数据
- 重新渲染当前页面数据
- 发送聊天包
聊天包格式在有写
WebSocket监听处理
前面提到发送消息时会发包,在WebSocket监听中,我们需要做如下几个事项
- 获取包中携带的data参数列表
- 重载全局ChatMessage数据(必要性,不然会导致Chat页和Message页数据不同步)
- 根据data中携带的title数据,在最近聊天数据列表中进行title查询索引
- 根据最近聊天列表索引进行替换相关最新信息
- 重载Chat页&Message页路由即可!
其实大致交流模块内容就是针对于WebSocket进行处理,在对于业务逻辑上,只要注意以上几个问题即可!