浏览 SDKs · Flutter
SDKsFlutter

消息概览

了解 OpenIM Flutter SDK 的 Message 模型、消息类型和消息生命周期。

复制

OpenIM Flutter SDK 的消息能力围绕 Message 展开。普通消息以及由本地路径或字节创建的媒体消息,使用对应的 create*Message() 创建,再通过 sendMessage() 发送。媒体文件已经由业务侧上传并取得 URL 时,使用 create*MessageByURL() 创建消息,再通过 sendMessageNotOss() 发送。两条路径都可以发送到单聊或群聊;新消息、撤回、删除、已读回执、上传进度和会话变化通过对应 listener 同步到应用状态。

Flutter 的 Message 模型没有 conversationID 字段。历史、搜索和会话状态使用的 conversationID 来自当前会话上下文;全局 listener 收到跨会话消息时,应根据 sessionTypegroupIDsendIDrecvID 和当前登录用户解析会话目标,再用 getConversationIDBySessionType() 校准。

消息页通常需要长期保存以下标识:

标识用途
clientMsgID消息在客户端侧的稳定 ID,用于渲染去重、状态更新、查询和分页游标;字段为空时不能作为合并键。
conversationID消息所属会话的记录 ID,用于读取历史消息、清理未读数和搜索;它来自会话上下文,不是 Message 字段。
recvID单聊发送目标用户 ID;发送群聊消息时不传。
groupID群聊发送目标群组 ID;发送单聊消息时不传。

消息类型

不同内容先通过对应的创建方法生成 Message。发送层不需要为每种内容维护不同入口,统一把创建方法返回的消息对象传给 sendMessage()sendMessageNotOss()

消息类型对比

内容类型创建方法内容元素
普通文本createTextMessage()textElem
@ 文本createTextAtMessage()atTextElem
图片createImageMessage()createImageMessageFromFullPath()createImageMessageByURL()pictureElem
音频createSoundMessage()createSoundMessageFromFullPath()createSoundMessageByURL()soundElem
视频createVideoMessage()createVideoMessageFromFullPath()createVideoMessageByURL()videoElem
文件createFileMessage()createFileMessageFromFullPath()createFileMessageByURL()fileElem
自定义消息createCustomMessage()customElem
引用、合并、位置、名片和表情对应 create*Message()quoteElemmergeElemlocationElemcardElemfaceElem

从本地路径或字节创建的媒体消息通常交给 SDK 上传并使用 sendMessage();媒体已经由业务上传并取得 URL 时,使用 create*MessageByURL() 创建,再调用 sendMessageNotOss(),避免重复进入 SDK 上传链路。

需要被其他成员收到的业务数据应放入消息内容或服务端扩展字段;只影响当前设备展示的状态可写入 localEx

Message 常用字段

字段说明
clientMsgID客户端消息稳定标识,用于发送状态、查询和事件合并。
serverMsgID服务端确认后的消息标识。
sendID / recvID / groupID发送者、单聊接收者和群组目标。
sessionType单聊或群聊会话类型;用于解析消息所属会话。
contentType消息内容类型,使用 MessageType 的数字值判断。
status消息发送状态。
sendTime / createTime服务端发送时间与本地创建时间。
isRead当前消息的已读状态。
textElempictureElemsoundElemvideoElemfileElem各消息类型的内容对象。
atTextElemquoteElemmergeElemcustomElem@、引用、合并和自定义内容。
offlinePush当前消息的离线推送配置。
ex / localEx服务端扩展与当前设备本地扩展。

clientMsgIDsendIDrecvIDgroupIDsessionType 在 Dart 模型中都是 nullable。写入索引前必须检查必要字段,不能用 ! 把异常载荷变成运行时崩溃。

消息生命周期与会话归属

  1. 使用 createTextMessage()createImageMessage() 等工厂创建 Message;创建本身不发送,也不触发新消息事件。
  2. 使用 sendMessage() 发送,或在自定义上传后使用 sendMessageNotOss()
  3. Future 成功后使用返回的 Message 更新发送端状态;其他端通过消息 listener 接收增量。
  4. 历史读取、搜索和按 ID 查询返回当前本地快照,不触发新消息事件。

当前聊天页已经持有 conversationID 时,直接把查询结果和发送结果合并到该会话。全局接收事件没有这一上下文,应先解析 sourceID:群聊使用非空 groupID;单聊根据 sendID 是否等于当前登录用户,在 recvIDsendID 中选择对端用户 ID。随后把 sourceIDsessionType 传给 getConversationIDBySessionType()

解析所需字段缺失时跳过该条消息并记录非敏感诊断信息。不要使用数组位置、展示名称或当前列表长度代替会话与消息标识。

按主题查看能力

发送消息

先创建消息,再发送到单聊或群聊。单聊填写 userID,群聊填写 groupID,另一个目标字段不传。

媒体文件由 SDK 上传时,使用本地路径或字节对应的创建方法与 sendMessage();媒体文件已由业务上传时,使用 create*MessageByURL()sendMessageNotOss()。两条路径都以 Future 返回的 Message 更新发送端状态,并按非空 clientMsgID 合并。

完整的参数、错误处理和发送边界见发送消息。图片、音频、视频、文件及富消息已经按类型拆分在“创建消息”菜单中。

接收消息

普通实时消息、登录后的离线同步消息和只在线消息分别由 onRecvNewMessageonRecvOfflineNewMessageonRecvOnlineOnlyMessage 处理。全局 listener 应先解析消息所属会话,再按 conversationID:clientMsgID 幂等合并;只在线消息不进入本地历史。

完整接收和异常载荷处理见接收消息

获取消息

历史消息通过 getAdvancedHistoryMessageList()conversationID 分页读取。第一页不传 startMsg;继续加载时使用边界消息作为下一页起点。历史结果与 listener 可能包含同一消息,必须使用相同的 clientMsgID 去重规则。

读取列表见加载历史消息,定位单条消息见按 ID 定位消息

搜索消息

searchLocalMessages() 搜索当前账号已经同步到本地的消息。群聊搜索使用群会话的 conversationID,不是发送消息时的 groupID;固定实现未使用的筛选字段不能描述为已经生效。

搜索流程见搜索消息

管理消息

已发送消息可以按实际能力执行转发、合并、删除、撤回、修改、置顶和清空历史,也可以插入只供本地展示的消息或发送输入状态。删除、撤回和修改事件都应按 conversationID:clientMsgID 更新原消息;置顶列表按 conversationID 替换,再按 clientMsgID 去重。

相关能力见创建转发消息创建合并消息删除消息撤回消息修改消息置顶或取消置顶消息插入本地单聊消息清理全部本地消息上报输入状态

标记已读

会话未读数通过 markConversationMessageAsRead() 清理。固定 Flutter SDK 支持接收单聊已读回执,但没有 WASM 的群消息成员级已读回执上报、已读/未读成员查询和群回执 listener,不能根据会话未读数推断群成员阅读情况。

单聊已读回执的处理也归入标记会话已读

提及其他用户

群聊 @ 消息使用 createTextAtMessage() 创建,再通过 sendMessage() 发送到群组。提及目标使用稳定 userID;接收端从 atTextElem 渲染成员,并通过会话的 groupAtType 展示 @ 提醒。

相关流程见创建 @ 消息

获取未读数

会话列表、总未读数和 @ 提醒来自会话 API 与 OnConversationListener。会话层按 conversationID 合并变化,并使用最新总未读计数更新全局角标;消息页面不应设置第二个会话 listener。

相关能力见维护总未读数

自定义消息与扩展数据

需要同步给其他成员的附加数据,在发送前写入自定义消息或服务端扩展字段;只影响当前设备展示的数据写入 localExsetMessageLocalEx() 写入完整字符串,不会自动合并旧值,也不会产生共享消息事件。

相关流程见创建自定义消息设置消息本地扩展

将音频转为文字

speechToText() 接收当前设备可读取的音频文件路径并返回 nullable 的识别文字。Flutter 的 speechToTextCapabilities() 只返回 Future<void>,不公开 WASM 能力对象中的格式、采样率、时长和大小字段。

转写结果可以通过 setMessageLocalContent() 写入语音消息的 soundElem.text,但只保存到当前设备,不会同步给其他成员或触发共享消息事件。完整边界见将音频转为文字

事件归属

消息事件按职责分散在对应能力页,概览页只说明导航和归属:新消息和离线同步见接收消息,删除见删除消息,撤回见撤回消息,修改见修改消息,置顶见置顶或取消置顶消息,单聊已读回执见标记会话已读,输入状态见上报输入状态。会话变化的完整监听见获取会话列表

创建消息对象和纯查询 API 直接使用 Future 返回值建立对象或快照,不应描述为触发共享事件。会改变状态的调用应分别处理 Future 结果、事件增量与重新查询校准。

Flutter enterprise SDK 公开消息修改、置顶会话消息和音频转写能力;这些页面的 API、model 与 listener 以固定 enterprise SDK commit 核对。当前 SDK 没有公开定向群消息创建 API,不应在客户端文档中模拟该能力。