sy_im_flutter_sdk 0.5.1
sy_im_flutter_sdk: ^0.5.1 copied to clipboard
SY IM Flutter SDK. Versioned pub.dev package over flutter_openim_sdk for login, messages, receipts, and groups.
sy_im_flutter_sdk #
SY IM Flutter SDK。在 Dart 里封装 flutter_openim_sdk,客户按 pub.dev 版本号 接入。
当前版本:0.5.1
客户在自己的 pubspec.yaml 里写下面这一行,然后执行 flutter pub get。不要下载、解压 sy-im-flutter-*.zip。
dependencies:
sy_im_flutter_sdk: ^0.5.1
暂时不能访问 pub.dev 时,用打过 tag 的 Git 依赖(tag 与版本号一致,例如 v0.5.1):
dependencies:
sy_im_flutter_sdk:
git:
url: https://github.com/carlcy/sy-im-flutter-sdk.git
ref: v0.5.1
本仓库的 example/ 为了本地改 SDK 后直接运行,使用 path: ../。这只给本仓库开发用,客户不要复制。
快速开始 #
流程与常见 IM Flutter SDK 一致:写依赖、初始化、用业务后端签发的 Token 登录、发一条文本、监听新消息。
1. 添加依赖 #
dependencies:
sy_im_flutter_sdk: ^0.5.1
path_provider: ^2.1.5
flutter pub get
path_provider 用来放 OpenIM 本地数据目录,由你的 App 自己声明。
2. 初始化 #
先向你的业务后端拿到 User JWT。SDK 再用它向 SY 控制面换 IM Token(POST /api/user/im/token)。响应里的 imApiAddr、imWsAddr 是 OpenIM 地址,客户端不要写死机器 IP。
import 'package:path_provider/path_provider.dart';
import 'package:sy_im_flutter_sdk/sy_im.dart';
late SyImEngine im;
Future<void> initSyIm() async {
final dir = await getApplicationDocumentsDirectory();
im = await SyIm.create(
appId: 'your_app_id',
apiBaseUrl: 'https://syrtcapi.shengyuchenyao.cn',
);
im.onConnectSuccess = () {
// 已连上 OpenIM
};
im.onConnecting = () {};
im.onConnectFailed = (code, error) {};
im.onKickedOffline = () {
// 被踢下线后重新 login
};
im.onUserTokenExpired = () {
// Token 过期:重新 getToken 再 login
};
im.onRecvNewMessage = (msgId, fromUserId, groupId, text) {
// 见「5. 接收消息」
};
im.onRecvC2CReadReceipt = (receipts) {
// 对方已读
};
final token = await im.getToken(
userId: 'user_001',
userJwt: userJwtFromYourServer,
);
await im.configureOpenIM(
imApiAddr: token['imApiAddr'] as String,
imWsAddr: token['imWsAddr'] as String,
dataDir: '${dir.path}/sy_im',
);
}
服务端持有 AppSecret 时,把 userJwt 换成 appSecret 即可走 POST /api/server/im/token。AppSecret 不要打进客户端安装包。
3. 登录 #
初始化完成后再登录。登录成功后才能发消息、拉会话、处理好友和群。
await im.login(
userId: 'user_001',
token: token['token'] as String,
);
token 是上一步 getToken 返回的 IM Token。
4. 发送消息 #
单聊填 toUserId,群聊填 groupId。
final msgId = await im.sendTextMessage(
toUserId: 'user_002',
text: 'hello',
);
5. 接收消息 #
在 configureOpenIM 之前设置 onRecvNewMessage(上面初始化示例已经设置)。新消息和离线同步下来的文本都会进这个回调。
im.onRecvNewMessage = (msgId, fromUserId, groupId, text) {
// fromUserId 发送方
// groupId 非空表示群消息
// text 文本内容
};
未读数会跟着 OpenIM 回调自己变,不用轮询。会话列表标题上直接听 unreadChanges:
im.unreadChanges.listen((update) {
final total = update.totalUnread;
for (final conversation in update.conversations) {
// conversation.unreadCount 是这一条会话的未读
}
});
await im.markConversationAsRead(
conversationId: conversations.first.conversationId,
);
// 返回时本地未读已经清零,随后再向 SDK 拉一次总数和会话列表
onUnreadChanged 是同一个快照的回调。totalUnread 是最近一次快照,不额外请求。单条会话的未读在 SyImConversation.unreadCount。
消息、会话和资料 #
这些接口都要求已经 login。原来的 sendTextMessage、login、getToken 签名不变。
await im.revokeMessage(conversationId: 'si_a_b', clientMsgId: msgId);
await im.sendAtTextMessage(
groupId: group.groupId,
text: '你好 @user_002',
atUserIds: ['user_002'],
);
await im.sendCustomMessage(
toUserId: 'user_002',
data: '{"kind":"order","id":"1"}',
description: '订单',
);
final hits = await im.searchMessages(keyword: 'hello');
await im.pinConversation(conversationId: 'si_a_b', pinned: true);
await im.setConversationDraft(conversationId: 'si_a_b', draft: '还没发出去');
await im.setConversationDoNotDisturb(
conversationId: 'si_a_b',
status: SyImRecvOpt.notReceive,
);
await im.setTyping(conversationId: 'si_a_b', typing: true);
final read = await im.getGroupMessageReadInfo(
conversationId: 'sg_group',
clientMsgId: msgId,
);
// read.hasReadCount / read.unreadCount;OpenIM Flutter 绑定不给成员 id
// 传 controlPlane(带 userJwt)时用控制面 who-read 花名册补 read.readUserIds,read.source == 'controlPlane'
final withRoster = await im.getGroupMessageReadInfo(
conversationId: 'sg_group',
clientMsgId: msgId,
controlPlane: im.controlPlane(userJwt: userJwtFromYourServer),
);
// 成员读了群消息后上报 seq,花名册才有他(不是全员已读名单)
await im.reportGroupMessagesRead(
controlPlane: im.controlPlane(userJwt: userJwtFromYourServer),
conversationId: 'sg_group',
seqs: [42],
);
await im.setSelfProfile(nickname: '阿花', ex: '{"level":1}');
await im.setGroupCustomInfo(groupId: group.groupId, notification: '公告', ex: '{}');
await im.addToBlacklist(userId: 'user_009');
已读回执(三端统一)。 onRecvReadReceipts(List<SyImReadReceipt>),字段 conversationId / userId(已读方)/ groupId(单聊 null)/ msgIds / readTime(毫秒,未知 0),与 Android ImReadReceipt、iOS SyImReadReceipt 相同;旧名 onRecvC2CReadReceipt 保留。群回执事件 onRecvGroupReadReceipt(conversationId, groupId, msgIds) 对应 Android / iOS 的 onRecvGroupReadReceipt。flutter_openim_sdk 3.8.3 没有群回执推送,所以 Flutter 只覆盖 watchGroupReadReceipts(conversationId:, clientMsgIds:, controlPlane:) 关注的消息:会话变化时立即检查,另每 5 秒兜底轮询,已读人数增加或出现新已读者时回调(首次检查只记基线);传 controlPlane 时新已读者再走 onRecvReadReceipts。用完调用 cancel(),logout() 会全部取消。SyImGroupReadInfo 三端同为 clientMsgId / hasReadCount / unreadCount / readUserIds / source(openim / controlPlane / none)。
@所有人 使用 syImAtAllUserId。撤回回调是 onMessageRevoked,正在输入回调是 onTypingChanged(SyImTypingStatus.typing / platformIds,platformIds 为空即停止)。sendTyping(conversationId:, focus:) 是与 Android / iOS 同名的写法,等同 setTyping。
好友申请与群 #
以下接口都要求已经 login。原有的 sendTextMessage、getConversations、login、logout、getToken 签名不变。控制面 REST(controlPlane.addFriend / createGroup / listFriends / history / revoke)也保持原路径。
await im.addFriend(userId: 'user_002', reason: '你好');
final incoming = await im.getFriendApplications();
await im.acceptFriendApplication(userId: incoming.first.fromUserId);
final group = await im.createGroup(
groupName: '项目群',
memberUserIds: ['user_002'],
);
await im.inviteToGroup(groupId: group.groupId, userIds: ['user_003']);
final members = await im.getGroupMembers(groupId: group.groupId);
还有 refuseFriendApplication、getFriends、joinGroup、kickGroupMembers、quitGroup、dismissGroup、getJoinedGroups。
表情回应与会话标签(控制面 lite) #
需要 ImControlPlane.userJwt。
reactToMessage(fromUserId:, emoji:, toUserId: / groupId:, targetClientMsgId: / targetSeq:, add:)→POST /api/user/im/reaction。服务端发一条 Custom(110)(description为sy_reaction_lite),对端在普通新消息里收到,用SyImReaction.parse(customData)解析。不是 OpenIM 原生回应(OpenIM 3.x 开源版没有),服务端不做聚合计数。- 会话标签(每个用户自己的会话分组,存在 SY 服务端,与置顶无关):
createConversationTag/listConversationTags/deleteConversationTag/addConversationsToTag/removeConversationsFromTag。
Android、iOS 同名同参。
错误码 #
ImControlPlane 的请求失败时抛 SyImControlPlaneException(仍是 Exception):code 为服务端业务码,httpStatus 为 HTTP 状态。取值在 SyImErrorCode,与 Android ImErrorCode、iOS SyImErrorCode 相同:
| code | 常量 | 含义 |
|---|---|---|
| 401 / 403 | unauthorized / forbidden |
JWT 无效 / 无权访问该应用 |
| 3001 | imNotEnabled |
应用未开通 IM |
| 3003 / 3004 | quotaMau / quotaMessages |
月活 / 消息量超出套餐 |
| 4003 | trialRetired |
体验版已下线 |
| 4005 | sensitiveRejected |
敏感词拦截 |
| 4006 | contentRejected |
发送前内容审核拒绝或审核服务不可达 |
| 4031 / 4032 / 4033 | credentialSuspended / Revoked / Expired |
AppId 访问凭证暂停 / 吊销 / 过期 |
| 4290 | rateLimited |
请求过于频繁 |
try {
await controlPlane.getToken(userId);
} on SyImControlPlaneException catch (e) {
if (e.isCredentialBlocked) { /* 凭证不可用 */ }
}
OpenIM 实时接口(登录、收发)的错误仍是 OpenIM 错误码。
跑本仓库示例 #
cd example
flutter pub get
flutter run --dart-define=SY_API_BASE=https://syrtcapi.shengyuchenyao.cn
示例里:粘贴 User JWT → 获取 IM Token → 初始化 → 登录 → 发送文本。OpenIM 地址以 Token 响应为准。
原生依赖 #
本包是纯 Dart 库,不内置 AAR / xcframework,也不再提供 SyImFlutterSdkPlugin MethodChannel。
原生 OpenIM 由依赖 flutter_openim_sdk: ^3.8.3+hotfix.15 按坐标拉取:
| 平台 | 坐标 |
|---|---|
| Android | Maven Central io.openim:core-sdk:3.8.3-patch15 |
| iOS | CocoaPods OpenIMSDKCore 3.8.3-hotfix.15 |
仓库里的 android/、ios/ 只是 0.3.0 移除空壳插件后的占位,Flutter 不会把它编进 App。OpenIM 的 gomobile 核心以 AAR / xcframework 形式发布,上游插件已经改成坐标引用,本包不再把这些二进制打进 zip。
发布到 pub.dev(维护者) #
客户能写 sy_im_flutter_sdk: ^0.5.1 之前,维护者需要把这个版本发到 pub.dev。代理不会代替你登录 pub.dev。
- 把
pubspec.yaml的version、CHANGELOG.md、VERSION、本 README 里的^x.y.z改成同一个版本。 - 在包根目录执行,确认没有 error:
dart pub publish --dry-run
- 用有权限发布
sy_im_flutter_sdk这个名字的 pub.dev 账号登录:
dart pub login
- 发布:
dart pub publish
- 打同名 tag,给 Git 依赖用:
git tag v0.5.1
git push origin v0.5.1
- 不要再上传
sy-im-flutter-0.4.2.zip这类压缩包给客户。集成方式只有上面的 pub.dev 行,或ref: v0.5.1的 Git 依赖。
首次发布会占用包名 sy_im_flutter_sdk。之后每次发版都要升版本号,pub.dev 不允许覆盖已发布版本。
说明 #
能力覆盖登录、单聊/群聊文本、已读回执、未读数、好友申请和基础群管理。离线推送要在 OpenIM 上配置 FCM / APNs 凭证。控制面默认域名是 https://syrtcapi.shengyuchenyao.cn,联调地址用 --dart-define=SY_API_BASE 覆盖,不要写进公开文档里的机器 IP。