会话
会话是两个用户之间(单聊)或一个群中(群聊)的消息流。本页介绍用 im.conversations 显示会话列表、未读数和草稿,置顶、标记为未读、删除会话,以及打开一个会话、查看会话详情。会话中的消息见消息,免打扰的设置见提醒、免打扰与举报。
典型用法:登录后调用 im.conversations.list() 取得会话列表,用 LiveListBuilder 显示,列表随新消息、已读、其他设备上的操作自动更新;用户点击一个会话时,用它的 conversationKey 打开消息列表。
会话列表和未读数都先从本机的数据库读取,App 冷启动、离线时也能立即显示,随后与服务端同步。
显示会话列表
im.conversations.list() 返回一个会持续更新的列表(ConversationList,即 LiveList<ConversationView>)。snapshot 是当前的快照,stream 在订阅时先发出当前快照、之后每次变化发出新快照,不再显示时调用 dispose() 释放。在 Flutter 界面中用 deeprespond_im_flutter 的 LiveListBuilder 按快照重建:
import 'package:deeprespond_im/deeprespond_im.dart';
import 'package:deeprespond_im/render.dart';
import 'package:deeprespond_im_flutter/deeprespond_im_flutter.dart';
import 'package:flutter/material.dart';
class ConversationsPage extends StatefulWidget {
const ConversationsPage({super.key, required this.im});
final DRClient im;
@override
State<ConversationsPage> createState() => _ConversationsPageState();
}
class _ConversationsPageState extends State<ConversationsPage> {
late final ConversationList _list = widget.im.conversations.list();
@override
void dispose() {
_list.dispose(); // 页面销毁时释放
super.dispose();
}
String _preview(ConversationView c) {
final draft = c.draft;
if (draft != null) return '[草稿] ${draft.text}';
final last = c.lastMessage;
if (last == null) return '';
final me = widget.im.auth.currentUser?.username ?? '';
return summarize(last, nameOf: (u) => displayName(widget.im, u, groupId: c.groupId), self: me);
}
@override
Widget build(BuildContext context) => LiveListBuilder<ConversationView>(
list: _list,
builder: (context, snapshot) {
if (snapshot.items.isEmpty && snapshot.loading) return const Center(child: CircularProgressIndicator());
return ListView.builder(
itemCount: snapshot.items.length,
itemBuilder: (context, i) {
final c = snapshot.items[i];
final mentioned = c.state?.mentionSeq != null;
final dot = c.unreadCount == 0 && c.state?.markedUnread == true;
return ListTile(
key: ValueKey(c.conversationKey),
leading: c.state?.pinnedAt != null ? const Icon(Icons.push_pin) : null,
title: Text(conversationTitle(widget.im, c)),
subtitle: Text('${mentioned ? '[有人@我] ' : ''}${_preview(c)}', maxLines: 1, overflow: TextOverflow.ellipsis),
trailing: c.unreadCount > 0
? Badge(label: Text('${c.unreadCount}'), backgroundColor: c.muted != null ? Colors.grey : null)
: (dot ? const Badge() : null),
onTap: () {
// 用 c.conversationKey 打开消息列表,见“消息”一页
},
);
},
);
},
);
}- 快照:
items是排好序的 ConversationView 列表;hasMore表示还有更多会话可以加载;loading表示正在加载;读取出错时error不为null。 - 对象不变:没有变化时
snapshot返回同一个对象;有变化时换成新的快照,其中没有变化的会话沿用原来的对象,可以用identical判断哪些行需要重建。快照和其中的列表都不能修改。 - 合并通知:同一轮事件循环中的多次变化合并为一次通知。
- 释放:列表由创建它的一方释放,
LiveListBuilder不会替你释放。退出登录、切换用户时 SDK 先把快照换成空列表并通知一次,然后释放列表,界面不会继续显示上一个用户的会话;重新登录后要再调用list()。未登录时调用list()抛出not_signed_in。 - 显示名称:
conversationTitle()、displayName()、summarize()来自package:deeprespond_im/render.dart,见消息。 - 不用
LiveListBuilder时,可以直接用StreamBuilder订阅stream,或者用toValueListenable()转为ValueListenable交给ValueListenableBuilder,见在 Flutter 界面中使用。
排序
- 置顶的会话在前,按置顶时间倒序;
- 其余的按最后一条消息的时间倒序,还没有消息的群按入群时间;
- 还没有服务端会话的单聊(见打开会话)按其中最新一条待发消息的时间,只有草稿的按草稿第一次保存的时间;
- 输入框中的草稿不改变已有会话的位置;
- 删除了的会话(
state.hidden为true)不显示,收到新消息时重新出现。
加载更多
第一次只把排在前面的会话读入内存(默认 1000 个,可以用创建客户端时的 conversationWindow 选项调整,见初始化、登录与连接)。用户翻到末尾时,如果 hasMore 为 true,调用 loadMore():SDK 先从本地再读 100 个,本地读完后再向服务端取更早的会话。
final s = list.snapshot;
if (s.hasMore && !s.loading) await list.loadMore();在新设备上第一次登录时,本地还没有全部会话,SDK 先同步最近的会话,更早的在 loadMore() 时从服务端获取。
未读数
每个会话的 unreadCount 是这个会话的未读消息数。群提示、通话记录、本人发的消息和发送时设置了 excludeFromUnread 的消息不计入。
- 标记为未读:
unreadCount为 0 而state.markedUnread为true时,显示一个未读圆点,见标记为未读。 - 有人 @ 我:
state.mentionSeq不为null时,会话中有 @ 本人或 @ 全体成员、本人还没有读到的消息,可以在摘要前显示“[有人@我]”。读到这条消息后它变回null;这条消息被撤回时,提醒退回到之前一条仍然有效的 @。 - 正在看的会话:用户正在看某个会话时(消息列表打开、停在最新处、在界面上显示,并且 App 在前台,见消息),这个会话的新消息不计入本地的未读数。请在用户看到新消息时标记已读,或者在创建客户端时开启
autoMarkRead,否则下一次同步后未读数会以服务端为准重新出现。
未读总数
im.conversations.getUnreadTotal() 同步返回全部会话的未读总数,用于底部导航栏的角标。总数变化时发出 ConversationsUnreadChanged 事件:
void updateBadge() => setTabBadge(im.conversations.getUnreadTotal(excludeMuted: true));
final sub = im.on<ConversationsUnreadChanged>().listen((_) => updateBadge());
updateBadge();
// 页面销毁时
await sub.cancel();- 总数覆盖全部会话,不只是已读入内存的那些;删除了的会话不计入。
- 只是标记为未读、没有未读消息的会话算 1。
excludeMuted: true时不计入设置了免打扰的会话,见提醒、免打扰与举报。- 在新设备上本地的会话列表还不完整时,SDK 用服务端统计的未读总数,这时
excludeMuted不起作用。事件的source为server时就是这种情况,本地的会话列表完整之后变为local。 - App 图标的角标:App 切到后台时,SDK 自动把未读总数(不计免打扰的会话)上报给服务端,推送带上这个角标数,App 不需要自己设置。要用别的数字时,在创建客户端时提供
badgeProvider;应用关闭了角标(运行配置的push_badge_enabled为false)时不上报。见离线推送。
标记已读
用户看到一个会话的最新消息时,调用 markRead() 把已读位置推进到最新一条:
im.conversations.markRead(conversationId);
// 只标记到某一条(如用户只看到了第 120 条)
im.conversations.markRead(conversationId, seq: 120);markRead()不等待、不返回结果。SDK 把同一会话的多次调用合并,至少间隔 1 秒发送一次;发送前先更新本地的未读数,已读位置已经是最新的不再发送。- 标记已读同时取消“标记为未读”和“有人 @ 我”。单聊开启了已读回执时,对方随之看到“已读”,见消息。
- 已打开的消息列表也有
markRead(),作用相同。也可以在创建客户端时设置autoMarkRead: true,由 SDK 在会话正在被看时自动标记。
把全部会话标记为已读:
final (:count, :truncated) = await im.conversations.markAllRead();count 为标记的会话数。服务端只处理当前所在的群和最近的 1000 个单聊和已离开的群,更早的会话还有未读时 truncated 为 true。markAllRead() 每分钟最多调用 2 次,超出时抛出 rate_limited(retryAfter 为需要等待的秒数)。
标记为未读
用户想稍后再处理一个会话时,可以把它标记为未读:
await im.conversations.setMarkedUnread(conversationId, true);
// 取消
await im.conversations.setMarkedUnread(conversationId, false);标记为未读不改变已读位置,对方看到的“已读”也不变。标记已读、清空聊天记录、删除会话时自动取消。设置在本人的全部设备间同步。
置顶会话
await im.conversations.setPinned(conversationId, true);
await im.conversations.setPinned(conversationId, false);置顶的会话排在列表最前面,state.pinnedAt 为置顶的时间。每个用户最多置顶 100 个会话,超出时抛出 limit_exceeded(reason 为 pinned_conversation_limit)。删除会话时自动取消置顶。
删除会话与清空聊天记录
// 从会话列表中删除,消息仍然保留
await im.conversations.remove(conversationId);
// 删除并清空聊天记录
await im.conversations.remove(conversationId, clearMessages: true);
// 只清空聊天记录,会话仍在列表中
await im.conversations.clearMessages(conversationId);- 删除会话:会话从本人的会话列表中移除,未读数清零,同时取消置顶和标记为未读。对方或群里有新消息时会话重新出现,删除前的消息仍可以翻看。
- 清空聊天记录:本人看不到此前的全部消息,本机保存的也一并删除;未读数清零。对方和其他群成员不受影响。
- 都只影响本人,在本人的全部设备间同步。
- 对还没有服务端会话的单聊(键以
draft:开头)调用remove(),只删除它的草稿。
草稿
用户在输入框中输入了文字但没有发出时,可以保存为这个会话的草稿,下次打开会话时恢复:
// 离开会话时保存(正在引用第 42 条消息)
await im.conversations.setDraft(conversationKey, Draft(text: controller.text, replyToSeq: 42));
// 打开会话时恢复
final view = await im.conversations.get(conversationKey);
controller.text = view?.draft?.text ?? '';
final replyTo = view?.draft?.replyToSeq;
// 发出后清除
await im.conversations.setDraft(conversationKey, null);- 草稿只保存在本机,不同步到其他设备。
- 草稿可以带
mentionUsernames、mentionAll(输入框中已 @ 的人)和replyToSeq(正在引用的消息),恢复时一起取回。text为空字符串且没有replyToSeq的视为清除。 - 会话列表中的
draft字段随之更新,可以在摘要处显示“[草稿]”。草稿不改变已有会话的排序。
免打扰的显示
用户对一个会话设置了免打扰后,ConversationView.muted 不为 null:muteMode 为 MuteMode.none 表示不提醒,MuteMode.mentionOnly 表示只在有人 @ 本人时提醒(只用于群),until 为到期时间(null 表示一直有效)。会话列表中可以显示一个免打扰图标,未读数显示为灰色圆点而不是数字。
免打扰由 im.push.mute() 设置,到期后 SDK 自动恢复并更新会话列表,见提醒、免打扰与举报。
正在输入
用户在输入框中输入时调用 typing(),对方的界面上显示“对方正在输入…”:
import 'dart:async';
import 'package:deeprespond_im/deeprespond_im.dart';
import 'package:flutter/material.dart';
class ChatTitle extends StatefulWidget {
const ChatTitle({super.key, required this.conversationKey, required this.title});
final String conversationKey;
final String title;
@override
State<ChatTitle> createState() => _ChatTitleState();
}
class _ChatTitleState extends State<ChatTitle> {
late List<String> _typing = im.conversations.getTyping(widget.conversationKey);
StreamSubscription<TypingChanged>? _sub;
@override
void initState() {
super.initState();
_sub = im.on<TypingChanged>().where((e) => e.conversationKey == widget.conversationKey).listen((e) => setState(() => _typing = e.usernames));
}
@override
void dispose() {
unawaited(_sub?.cancel());
super.dispose();
}
@override
Widget build(BuildContext context) => Text(_typing.isNotEmpty ? '对方正在输入…' : widget.title);
}
// 输入框:每次输入时报告
Widget input(String conversationKey) => TextField(onChanged: (_) => im.conversations.typing(conversationKey));typing()可以在每次输入时调用,SDK 每 3 秒最多发送一次;长连接没有连上时直接丢弃。单聊和 100 人以内的群可用。- 收到的“正在输入”5 秒内没有再收到就自动消失。
im.conversations.getTyping(conversationKey)同步返回这个会话中正在输入的人,会话列表中每一项的typing字段相同。
打开会话
会话列表中的每一项都有 conversationKey,用它打开消息列表。从其他地方(如联系人、群列表)进入聊天时:
单聊:用 openSingle() 按对方的用户名取得会话:
final view = await im.conversations.openSingle('lisi');
final messages = im.messages.open(view.conversationKey);两人之间已经聊过时返回已有的会话;还没有会话时返回一个本地的“草稿会话”,conversationKey 为 draft:lisi,conversationId 和 state 为 null。在草稿会话中发出第一条消息后,服务端创建正式的会话,SDK 自动把草稿会话换成正式会话:
- 会话列表中草稿会话被正式会话替换,位置不变,
ConversationsChanged事件同时带上被移除的草稿键和新的会话 ID; - 用草稿键打开的消息列表自动切换到正式会话,它的
conversationKey变为新的会话 ID; - 之后用旧键调用
typing()、setDraft()、getTyping()、im.messages.send()等方法时,SDK 自动换成新的会话 ID。
草稿会话只在有草稿或待发消息时出现在会话列表中;只是打开、什么也没写的不出现。对方先发来消息,或本人在其他设备上先和他聊了的,草稿会话同样并入正式会话。
群聊:群会话的 ID 就是群 ID,直接用群 ID 打开:
final messages = im.messages.open(groupId);会话详情
im.conversations.get() 从本机读取一个会话:
final view = await im.conversations.get(conversationId);
final state = view?.state;
if (view != null && state != null) {
if (view.conversationType == 'group' && state.membership != 'member') {
showToast('你已不在群中'); // left 已退出 / removed 已被移出 / dismissed 群已解散
}
if (view.conversationType == 'single' && view.peer == null) {
showToast('对方已注销');
}
print('对方已读到 ${state.peerReadSeq},可见范围 ${state.startSeq}~${state.maxSeq}');
}- 本机没有这个会话时返回
null。会话列表中的每一项与它相同。 state是服务端的会话状态(ConversationState),如已读位置、对方已读到的位置、在群里的身份。服务端新增的字段可以从state.raw读取。- 已离开的群:退出、被移出或群解散后,会话仍在列表中,
state.membership不再是member。可以翻看离开前的消息、标记已读、置顶和删除会话,但不能再发送消息、撤回、表情回应和置顶消息。 - 对方已删除:单聊的对方被删除后
peer为null,用conversationTitle()显示为“已删除的用户”,不能再发送。
需要立即从服务端取最新状态时(如收到你的业务服务端的通知),调用 refresh():
final latest = await im.conversations.refresh(conversationId);通常不需要调用:SDK 通过长连接实时更新会话状态,断线重连、回到前台后自动同步。
监听会话的变化
会话列表会自动更新,大多数情况下只需订阅列表。要在其他地方响应会话的变化(如更新另一个组件),可以监听事件:
im.on<ConversationsChanged>().listen((e) {
// upserted:新增或有变化的会话键;removed:从本机移除的会话键(含并入正式会话的草稿键)
print('${e.upserted} ${e.removed}');
});事件在本地数据更新之后发出,只说明哪些会话变了,最新的内容用 get() 或列表的快照读取。全部事件见事件与错误处理。
接口参考
im.conversations 的方法。除 list()、markRead()、getUnreadTotal()、getTyping()、typing() 之外都返回 Future,失败时抛出 DRException;没有登录时为 not_signed_in,网络错误为 network_error、timeout。错误码见事件与错误处理和服务端的错误码。
参数中的 conversationId 是服务端的会话 ID;conversationKey 是会话列表中的键,正式会话就是会话 ID,还没有服务端会话的单聊为 draft:{username}。
im.conversations.list()
取得会话列表。每次调用返回一个新的列表对象,不再使用时调用它的 dispose()。
返回值:ConversationList,即 LiveList<ConversationView>:
| 成员 | 说明 |
|---|---|
snapshot | 当前的快照 LiveListSnapshot<ConversationView>:items、hasMore、loading、error |
stream | Stream<LiveListSnapshot<ConversationView>>:订阅时先发出当前快照,之后每次变化发出新快照;释放后结束 |
loadMore() | 加载更多会话,返回 Future<void>;没有更多或正在加载时立即返回 |
dispose() | 释放列表。释放后 loadMore() 抛出 invalid_state(reason 为 disposed) |
可能的错误:未登录时同步抛出 not_signed_in。
im.conversations.get()
从本机读取一个会话。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId | String | 是 | 会话 ID;也可以是 draft:{username},返回这个草稿会话(没有草稿和待发消息时为 null) |
返回值:Future<ConversationView?>,本机没有时为 null。
可能的错误:not_signed_in、storage_error。
im.conversations.openSingle()
按对方的用户名取得单聊会话:本机有的直接返回;没有的向服务端查询,两人之间还没有会话时返回草稿会话。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | String | 是 | 对方的用户名,不区分大小写 |
返回值:Future<ConversationView>。草稿会话的 conversationKey 为 draft:{username}(小写),conversationId、state 为 null。用户名不存在、已删除或就是本人时同样返回草稿会话,发送时才会失败。
可能的错误:local_validation(reason 为 required,用户名为空)、network_error。
im.conversations.refresh()
从服务端重新获取会话状态,合并到本机后返回。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId | String | 是 | 会话 ID |
返回值:Future<ConversationView>。
可能的错误:not_found(会话不存在或本人不可见)、local_validation(reason 为 not_found,服务端返回了会话但本机没有保存)、network_error。
im.conversations.markRead()
把已读位置推进到最新一条或指定的一条。不等待、不返回结果,SDK 合并发送,失败时只记日志。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId | String | 是 | 会话 ID。草稿会话和未登录时什么也不做 |
seq | int? | 否 | 命名参数,已读到的序号,默认为最新一条 |
返回值:无(void)。
im.conversations.markAllRead()
把全部会话标记为已读。成功后 SDK 立即同步一次会话状态。
返回值:Future<({int count, bool truncated})>。count 为标记的会话数;truncated 为 true 时还有更早的会话没有处理,本机的未读数等同步后再更新。
可能的错误:rate_limited(每分钟超过 2 次,source 为 ErrorSource.local,retryAfter 为需要等待的秒数)、network_error。
im.conversations.setMarkedUnread()
标记为未读或取消标记。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId | String | 是 | 会话 ID |
marked | bool | 是 | true 标记为未读,false 取消 |
返回值:Future<void>。
可能的错误:not_found、network_error。
im.conversations.setPinned()
置顶或取消置顶会话。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId | String | 是 | 会话 ID |
pinned | bool | 是 | true 置顶,false 取消 |
返回值:Future<void>。
可能的错误:limit_exceeded(reason 为 pinned_conversation_limit,已置顶 100 个)、not_found、network_error。
im.conversations.remove()
删除会话,可以同时清空聊天记录。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId | String | 是 | 会话 ID;为 draft:{username} 时只删除草稿 |
clearMessages | bool | 否 | 命名参数,同时清空聊天记录,默认 false |
返回值:Future<void>。
可能的错误:not_found、network_error。
im.conversations.clearMessages()
清空本人在这个会话中的聊天记录,会话仍在列表中。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId | String | 是 | 会话 ID |
返回值:Future<void>。
可能的错误:not_found、network_error。
im.conversations.setDraft()
保存或清除会话的草稿(只保存在本机)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationKey | String | 是 | 会话键,可以是草稿会话的 draft:{username} |
draft | Draft? | 是 | 草稿,见 Draft;null 清除 |
返回值:Future<void>。
可能的错误:not_signed_in、storage_error。
im.conversations.getUnreadTotal()
同步返回未读总数。变化时发出 ConversationsUnreadChanged。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
excludeMuted | bool | 否 | 命名参数,不计入免打扰的会话,默认 false。用服务端的总数时不起作用 |
返回值:int。
im.conversations.getTyping()
同步返回一个会话中正在输入的人。变化时发出 TypingChanged。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationKey | String | 是 | 会话键 |
返回值:List<String>,正在输入的人的用户名,没有时为空列表。
im.conversations.typing()
告诉对方本人正在输入。每 3 秒最多发送一次,长连接没有连上时直接丢弃。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationKey | String | 是 | 会话键;草稿会话同样可用 |
返回值:无(void)。
事件
| 事件 | 字段 | 说明 |
|---|---|---|
ConversationsChanged | upserted、removed(List<String>) | 会话列表中的会话变化,键为会话 ID 或 draft:{username} |
ConversationsUnreadChanged | total(int)、source(String) | 未读总数变化;total 按 getUnreadTotal() 的默认规则(计入免打扰的会话),source 为 local 或 server |
TypingChanged | conversationKey(String)、usernames(List<String>) | 正在输入的人变化 |
用 im.on<ConversationsChanged>() 这样按类型订阅,返回 Stream,不再需要时取消订阅。
数据结构
各类型都是服务端 JSON 的类型化视图:常用字段有 camelCase 的 getter,原始的 JSON 在 raw 中(不可修改)。时间字段为 UTC 的 DateTime。
ConversationView
会话列表中的一项,由服务端的会话状态和本机的数据组合而成。
| 字段 | 类型 | 说明 |
|---|---|---|
conversationKey | String | 会话键:正式会话为会话 ID;还没有服务端会话的单聊为 draft:{username} |
conversationId | String? | 会话 ID,草稿会话为 null |
conversationType | String | single 或 group |
peer | String? | 单聊的对方用户名,对方已删除时为 null;群聊为 null |
groupId | String? | 群聊的群 ID;单聊为 null |
state | ConversationState? | 服务端的会话状态,见 ConversationState;草稿会话为 null |
lastMessage | MessageView? | 最后一条消息,包括发送队列中还没发出的;见消息 |
unreadCount | int | 未读消息数(不含“标记为未读”) |
draft | Draft? | 本机的草稿 |
muted | MuteInfo? | 会话免打扰,没有设置或已到期为 null,见 MuteInfo |
typing | List<String> | 正在输入的人 |
raw | Map<String, Object?> | 原始数据 |
ConversationState
本人在一个会话中的状态,与服务端的用户会话对象相同。
| 字段 | 类型 | 说明 |
|---|---|---|
conversationId | String | 会话 ID;群会话的 ID 就是群 ID |
conversationType | String | single 或 group |
peer | String? | 单聊的对方用户名,对方已删除时为 null |
groupId | String? | 群聊的群 ID |
membership | String? | 群聊中本人的身份:member(成员)、left(已退出)、removed(已被移出)、dismissed(群已解散)。单聊为 null |
startSeq | int | 本人能看到的第一条消息的序号(入群、清空聊天记录、消息过期都会让它变大) |
maxSeq | int | 本人能看到的最后一条消息的序号 |
readSeq | int | 本人的已读位置 |
unreadCount | int | 服务端统计的未读数。界面请用 ConversationView.unreadCount,它包含了本机刚收到的消息 |
markedUnread | bool | 是否标记为未读 |
peerReadSeq | int? | 单聊中对方已读到的序号;应用没有开启单聊已读回执时为 null。群聊为 null |
mentionSeq | int? | 本人还没读到的、@ 了本人或全体成员的消息的序号 |
clearedSeq | int | 清空聊天记录的位置,0 表示没有清空过 |
hiddenSeq | int | 删除会话时的位置,0 表示没有删除过 |
hidden | bool | 会话是否处于删除状态 |
pinnedAt | DateTime? | 置顶的时间 |
joinedAt | DateTime? | 群聊中本次入群的时间 |
leftAt | DateTime? | 群聊中离开的时间 |
messageVersion | int | 会话中消息的变更版本号 |
lastMessageAt | DateTime? | 最后一条消息的时间 |
version | int | 状态的版本号,SDK 同步用 |
Draft
用 const Draft(text: '...') 创建。
| 字段 | 类型 | 说明 |
|---|---|---|
text | String | 必填,输入框中的文字 |
mentionUsernames | List<String>? | 已 @ 的人 |
mentionAll | bool | 是否已 @ 全体成员,默认 false |
replyToSeq | int? | 正在引用的消息的序号 |
createdAt | DateTime? | 第一次保存的时刻,由 SDK 填写,传入的忽略 |
updatedAt | DateTime? | 最近一次保存的时刻,由 SDK 填写 |
MuteInfo
| 字段 | 类型 | 说明 |
|---|---|---|
mode | String | none 不提醒;mention_only 只在 @ 本人时提醒(只用于群) |
muteMode | MuteMode? | mode 的枚举形式:MuteMode.none、MuteMode.mentionOnly;不认识的取值为 null |
until | DateTime? | 到期时间,null 表示一直有效 |
LiveListSnapshot
| 字段 | 类型 | 说明 |
|---|---|---|
items | List<T> | 条目,不可修改 |
hasMore | bool | 还有更多可以加载 |
loading | bool | 正在加载 |
error | DRException? | 最近一次读取的错误 |
