主题
XHIM Flutter UI Kit
xhim_flutter_ui 是建立在 xhim_flutter 无头 SDK 之上的可选 Material UI Kit。 业务应用可以只接 Core plugin,也可以引入本包快速获得会话列表、聊天时间线、 稳定的历史消息续页、输入组件、骨架屏、未读角标和自定义消息渲染入口。 该包同时提供通讯录、好友申请、个人资料、建群选人、群聊列表、群成员、 基于角色的群治理、个人/群二维码和全屏媒体浏览组件。
yaml
dependencies:
xhim_flutter: 0.1.0-alpha.1
xhim_flutter_ui: 0.1.0-alpha.1dart
final client = await XHIMFlutterClient.connect(
server: 'https://im.example.com',
userId: account.userId,
credentialProvider: account.fetchXHIMCredential,
);
final source = XHIMClientUIKitDataSource(client);
final conversations = XHIMConversationListController(dataSource: source);
XHIMConversationListView(
controller: conversations,
presenter: (item) => XHIMConversationPresentation(
title: appProfiles.displayNameFor(item.id),
),
onConversationTap: openConversation,
);聊天页使用服务端历史强续页,不会从最老消息开始加载:
dart
final chat = XHIMChatController(
dataSource: source,
conversationId: conversationId,
currentUserId: account.userId,
);
XHIMChatView(
controller: chat,
rendererRegistry: XHIMMessageRendererRegistry(
builders: {
'acme/order': (context, message) => OrderCard(message.payload),
},
),
composerActions: XHIMComposerActions(
onPickImage: pickAndSendImage,
onTakePhoto: takeAndSendPhoto,
onPickFile: pickAndSendFile,
onRecordAudio: recordAndSendAudio,
),
);应用前后台与账号状态
App 根组件创建完 Client 后绑定一次前后台上报。这是设备运行状态, 不是用户 Presence:
dart
final runtimeBinding = XHIMAppRuntimeLifecycleBinding.bind(client);账号停用或注销使用应用级 Binding 统一清除自动登录凭证、退出 Client 并回到登录页。提示文案是固定安全文案,不会显示管理员填写的原因:
dart
final accountBinding = XHIMAccountLifecycleBinding.bind(
client,
clearRememberedCredentials: account.clearRememberedCredentials,
onNotice: (notice) async {
await navigator.showAccountState(notice.title, notice.message);
navigator.replaceWithLogin();
},
);账号 Session 销毁时同时 dispose() 两个 Binding,避免旧账号事件进入新页面。
可信业务通知
XHIMMessageRendererRegistry 默认把服务端签发的 typed BusinessNotification 渲染为通知卡片。默认只展示 title/body,不把 dataJSON 放进界面、日志或通用长按菜单;宿主确实需要跳转时,在固定 白名单回调中处理:
dart
final renderers = XHIMMessageRendererRegistry(
onBusinessNotificationTap: (notification) {
businessRouter.openWhitelisted(notification.dataJSON);
},
);未知、未来或损坏版本不会强行解析,仍显示通用 fallbackText。用户资料 被标记为 notificationService 时,默认资料页不提供“添加好友”。
通讯录与群聊
UI Kit 从 SDK 的本地投影分页查询好友和群聊,并批量解析用户资料。 默认显示顺序是“好友备注 -> 用户昵称 -> XH用户”,不会把内部 user_id 当作界面名称。
dart
final contacts = XHIMContactsController(
dataSource: source,
currentUserId: account.userId,
);
XHIMContactsView(
controller: contacts,
onFriendRequestsTap: openFriendRequests,
onGroupsTap: openGroups,
onContactTap: openContactProfile,
);通讯录点击好友后进入完整资料页,不直接跳聊天;备注优先于对方昵称,内部 user_id 永远只用于 SDK 调用:
dart
final profile = XHIMProfileController(
dataSource: source,
currentUserId: account.userId,
userId: friendship.peerUserId,
friendship: friendship,
);
XHIMProfilePage(
controller: profile,
onConversationReady: openConversation,
);XHIMProfilePage 对本人提供资料编辑入口,对好友提供“发消息”,对陌生人 提供“添加好友”。公开界面使用 publicUserId(XH 号)、昵称、备注和部门, 不会展示内部 ID。
建群时当前用户由服务端自动成为群主,页面只选择需要邀请的好友:
dart
final creator = XHIMCreateGroupController(
dataSource: source,
currentUserId: account.userId,
);
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => XHIMCreateGroupPage(controller: creator),
),
);群聊信息页使用 XHIMGroupDetailsController。群主、管理员和普通成员的 可见操作会根据 XHIMGroupMemberRole 分开;设置管理员、禁言、 转让群主、移除成员、入群审批和解散群聊仍会由服务端每次独立鉴权。 UI 隐藏按钮不会被当成安全边界。
dart
final details = XHIMGroupDetailsController(
dataSource: source,
group: selectedGroup,
currentUserId: account.userId,
);
XHIMGroupDetailsView(
controller: details,
onMemberTap: openMemberProfile,
onAddMembers: openFriendPicker,
onGroupExited: closeDetails,
);XHIMContactsView.presenter 可插入购买方的拼音分组器,以生成字母索引 需要的 sectionKey;UI Kit 不强制引入特定拼音库。
媒体消息预览
视频、图片、语音和文件有内置卡片。宿主 App 通过 XHIMMediaPresenter 提供已审核的缩略图、时长、文件名、传输进度和点击操作:
dart
final renderers = XHIMMessageRendererRegistry(
mediaPresenter: (message) => mediaStore.presentationFor(message),
builders: {
'acme/order': (context, message) => OrderCard(message.payload),
},
);图片使用内置的 XHIMMediaViewerPage.image 获得全屏、缩放和拖动。视频播放器 通过 XHIMMediaViewerPage.custom 或 openXHIMVideoViewer 注入,宿主可以继续 使用自己审计过的缓存、请求头、DRM 和播放器实现;Core 事件不会暴露私有缓存 路径。
dart
mediaPresenter: (message) => XHIMMediaPresentation(
thumbnail: mediaCache.thumbnail(message),
duration: mediaCache.duration(message),
progress: mediaCache.progress(message),
onTap: () => openXHIMImageViewer(
context,
image: mediaCache.fullImage(message),
),
),UI Kit 不直接依赖某个相册、相机、文件或录音插件。购买方可选择团队已经审计的 Flutter 插件,并在 action callback 中调用 XHIMFlutterClient.sendImage、 sendVideo、sendAudio 或 sendFile。这样权限文案、压缩策略和隐私清单仍由 最终 App 控制。
个人与群聊二维码
二维码载荷由服务端签发并解析,Flutter 不自行解密、不信任二维码里的目标 ID。 生产账号层把当前短期访问凭证通过 Provider 交给默认 HTTP 适配器:
dart
final qr = XHIMHttpQRCodeDataSource(
server: Uri.parse('https://im.example.com'),
accessTokenProvider: account.fetchXHIMCredential,
);
XHIMQRCodePage(
title: '我的二维码',
loader: qr.createUserQRCode,
);
final result = await Navigator.push<XHIMResolvedQRCode>(
context,
MaterialPageRoute(builder: (_) => XHIMQRCodeScannerPage(dataSource: qr)),
);解析后使用 XHIMQRCodeResultPage:用户结果进入资料页;已加入群直接打开会话; 未加入群调用 requestGroupJoin,是否直接加入、是否等待审批由服务端返回结果决定。
扫码需要宿主 App 声明相机权限:
- iOS:
Info.plist添加NSCameraUsageDescription;最低系统仍可设为 iOS 15。 - Android:应用清单添加
android.permission.CAMERA,并使用 compileSdk 34 或更高。 - Web:必须部署 HTTPS,并由浏览器授权摄像头。
UI Kit 当前使用 mobile_scanner 7.4.0、qr_flutter 4.1.0 和 http 1.6.0; 因此最低 Dart 为 3.7、Flutter 为 3.29。无头 xhim_flutter 仍保持更低的 SDK 约束,不需要扫码 UI 的购买方不会被这些依赖约束。
当前 Alpha 已闭环会话、单聊、通讯录、好友申请、个人资料、建群选人、 群聊列表、群成员、群权限、二维码、媒体卡片和全屏浏览主链。正式资源 制品签名、私有 pub 发布和真机回归仍属发布流水线事项;完成发布准入前 本包仍保持 Alpha 标识。
发布验证
仓库统一门禁会把 Core plugin 与 UI Kit 复制到纯 ASCII 临时目录,执行格式化、 静态分析、单元/Widget 测试和两个 pub 发布预检:
bash
./scripts/release/validate_flutter_package.sh2026-08-06 的本机结果为 Core 7/7、UI Kit 10/10,两个包均为 0 warning; UI Kit 因验证未发布的同仓 Core 包而使用 pubspec_overrides.yaml,发布预检会给出 1 条预期 hint。另有全新消费者 Android Debug APK 与 iOS 15 Simulator App 消费构建通过。扫码仍必须在最终 App 中完成相机权限文案和物理设备回归,模拟器 编译结果不能代替权限拒绝、旋转、后台恢复和真机摄像头验证。