跳至主要内容

TWT Visitor SDK

在 App 内打开访客聊天页面。宿主传入 HTTPS 地址后,SDK 使用系统 WebView / WKWebView 加载,并处理导航、权限、下载回传与站点数据清理。

不使用 SDK、App 自建 WebView 打开访客页,见 访客端 WebView 自行接入。

当前版本:0.0.1

平台要求集成
AndroidminSdk 23Maven Central:io.github.twt-chat:visitor-sdk:0.0.1
iOSiOS 15+SPM:https://github.com/TWT-Chat/twt-visitor-ios.git(tag 0.0.1,产品 TwtVisitorSDK)
FlutterFlutter ≥ 3.44,Dart ^3.12.2Git:https://github.com/TWT-Chat/visitor_flutter.git(ref 0.0.1)

集成​

Android​

dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}

dependencies {
implementation("io.github.twt-chat:visitor-sdk:0.0.1")
}

SDK 已合并 INTERNET、RECORD_AUDIO、CAMERA(required=false)。Android 9 及以下若下载到公共 Downloads,请自行添加 WRITE_EXTERNAL_STORAGE。

iOS​

Xcode → File → Add Package Dependencies,填入:

https://github.com/TWT-Chat/twt-visitor-ios.git

Dependency Rule 选择 Exact Version 0.0.1,产品勾选 TwtVisitorSDK。Deployment Target ≥ iOS 15。

在 Info.plist 中声明:

<key>NSMicrophoneUsageDescription</key>
<string>用于发送语音消息</string>
<key>NSCameraUsageDescription</key>
<string>用于拍摄并发送照片或视频</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>用于保存会话图片</string>
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>

请保持 App Transport Security 默认设置。公开 API 须在主线程调用。

Flutter​

dependencies:
visitor_flutter:
git:
url: https://github.com/TWT-Chat/visitor_flutter.git
ref: 0.0.1
flutter pub get

Android 端需配置 mavenCentral()(见上方 Android 集成)。若使用 RepositoriesMode.PREFER_SETTINGS,还须保留 Flutter Engine 仓库 https://storage.googleapis.com/download.flutter.io。iOS 端无需再添加 SPM,按 iOS 一节配置 Info.plist 即可。


快速开始​

接入方只需搞清三件事:

  1. url(必填):控制台配置的 URL 地址,必须 HTTPS。
  2. 配置项:SDK 认识的字段(isApp / language / theme / title / directChatId / newMessageSoundMode)。都有默认值,可不传。
  3. query:给访客页的业务参数。SDK 不解释 key,只负责 UTF-8 编码拼进 URL。匿名访客可以不传。

不要自己拼 ?a=1&b=2。不要把密码、Cookie、AppSecret 或长期 token 放进 URL。

最少打开(只传 url)时 SDK 拼出:

控制台配置的 URL 地址?lang=zh-cn&theme=system

lang=zh-cn、theme=system 由默认配置写入。App 内若要显示关闭按钮,再设 isApp = true。

Android​

val handle = TwtVisitorSdk.start(
this,
VisitorConfig(url = "控制台配置的 URL 地址"),
)

start 打开 VisitorActivity 并返回 VisitorHandle。传入非 Activity Context 时会自动加上 FLAG_ACTIVITY_NEW_TASK。下载、新消息等回调见「回调与事件」。

iOS​

import TwtVisitorSDK

let config = VisitorConfiguration(
url: URL(string: "控制台配置的 URL 地址")!
)
let handle = try TwtVisitorSDK.present(from: self, configuration: config, delegate: self)

由宿主 dismiss 页面;dismiss 之后 handle 失效。

Flutter​

嵌入当前页面:

import 'package:visitor_flutter/visitor_flutter.dart';

final config = VisitorConfiguration(
url: '控制台配置的 URL 地址',
);

Expanded(child: VisitorView(configuration: config))

VisitorView 必须放在有确定尺寸的父布局中(如 Expanded、SizedBox)。

打开独立原生页:

final handle = await VisitorFlutter.present(configuration: config);

独立页没有 close,由系统返回或页面内关闭按钮结束。


配置项​

三端字段相同。SDK 会把部分配置写成 URL query,并覆盖 query 里的同名 key。

字段默认进 URL?说明
url空(必填)基址控制台配置的 URL 地址,仅 HTTPS。HTTP、file、javascript、自定义 scheme、带 user:pass 的地址会被拒绝
query空 Map原样拼接业务参数,见下一节。不要手拼 ?a=1
isAppfalsetrue 时写 is_app=1App 内打开,访客页显示关闭按钮。false 不写 is_app=0
languagezh-cn每次都写 langen / zh-cn / zh-tw / ja / ko / de / fr / pt / ru / es / vi / th / id / ms / tl
themesystem每次都写 themelight / dark / system。运行时 setTheme 只接受 light / dark
titlenull否原生标题栏文案;空则使用网页标题
directChatIdnull有值时写 direct=1&chatid=直达会话,并去掉 chat_id
newMessageSoundModeweb一般否web:页面播放;native:宿主在新消息回调中播放。走 JS 桥,不靠 URL(Flutter Android present() 会额外带 new_message_sound_mode)

写入顺序:url 自带 query → config.query → is_app / lang / theme / direct+chatid。后者覆盖前者。

query 业务参数​

query 是透明 Map,SDK 只编码拼接,不解释 key。匿名访客可以整份不传。is_app / lang / theme / direct / chatid 用配置项,不要再写进 query。

绑业务用户时:

query = mapOf(
"sbs" to "user-123",
"sbs_mm" to signature,
"ranstr" to randomStr,
"name" to "张三",
)
参数必填?含义
sbs绑用户时必填业务系统里的用户唯一 ID,访客页用它识别已登录客户
sbs_mm有 sbs 就要sbs 的签名
ranstr有签名就要参与签名的随机串
name可选访客姓名,进入客服工作台展示
nickname可选访客昵称
email可选访客邮箱
phone可选访客手机号
customer_remark可选客户备注,随登录提交给客服侧
ext_fk_id可选已有访客 ID,用来续上该访客的历史会话
referer可选来源页 URL(也认 referer_url),客服侧显示为来源链接
source_title可选来源页标题

不要传 visitor_id、source=android / ios / flutter:访客页不读。App 标识用配置项 isApp=true。


会话 API​

能力AndroidiOSFlutter
打开独立页TwtVisitorSdk.startTwtVisitorSDK.presentVisitorFlutter.present
嵌入—makeEmbeddedController + registerVisitorView
切换主题handle.setThemehandle.setThemeVisitorFlutter.setTheme / handle.setTheme
下载回传handle.reportDownloadStatus同左VisitorFlutter.reportDownloadStatus
关闭handle.close()handle.close()独立页走系统返回;嵌入页由宿主 pop
清理站点数据TwtVisitorSdk.clearSiteDataTwtVisitorSDK.clearSiteDataVisitorFlutter.clearSiteData

下载状态:started / completed / failed / cancelled。requestId 必须与请求一致。可选字段:path / progress / sourceUrl / mimeType / bytes / errorCode / message。iOS 相册完成路径为 photos://localIdentifier。

清理结果:SUCCESS / INVALID_URL / UNSUPPORTED / IN_USE / FAILED(Flutter 为小写驼峰)。IN_USE 表示仍有会话占用该 host。

切换账号:关闭页面 → 等待销毁完成 → clearSiteData 成功 → 再打开下一个账号。


回调与事件​

回调在主线程。请按 requestId / messageId 去重;页面销毁时把未完成的下载标为 failed。

Android VisitorBridgeListener

  • onDownloadRequested:开始下载,完成后 reportDownloadStatus
  • onNewMessage:新消息
  • onBack():返回键。true 表示宿主已处理
  • onPermissionResult:权限结果

iOS VisitorBridgeDelegate(弱引用)

  • visitorDownloadRequested(...)
  • visitorNewMessage()(可能重复)
  • visitorBack() -> Bool
  • visitorPermissionResult(...)

Flutter VisitorFlutter.events

无监听时不缓存事件,进入页面即 listen,离开时 cancel。

late final StreamSubscription<VisitorBridgeEvent> sub;

@override
void initState() {
super.initState();
sub = VisitorFlutter.events.listen((e) {
switch (e.type) {
case 'downloadRequested':
break;
case 'webReady':
case 'webLoadFailed':
case 'close':
case 'new_message':
break;
}
});
}

@override
void dispose() {
sub.cancel();
super.dispose();
}

事件名:downloadRequested、webReady、webLoadFailed、close、new_message、theme。

嵌入式页面处理返回:

PopScope(
canPop: false,
onPopInvokedWithResult: (_, __) async {
if (!await VisitorFlutter.handleBack()) {
if (context.mounted) Navigator.of(context).pop();
}
},
child: VisitorView(configuration: config),
)

handleBack() == true 表示页面已消费返回(例如从会话详情回到列表)。首次加载失败可调用 VisitorFlutter.reload()。


行为说明​

导航 同域 HTTPS 留在容器内;跨域 HTTPS 使用系统浏览器;其它 scheme 拦截。

存储 Cookie、LocalStorage、IndexedDB 默认持久化。第三方 Cookie 与混合内容关闭。

权限 仅同域 HTTPS 的麦克风 / 摄像头采集会进入系统授权,跨域一律拒绝。 例外:Flutter 在 Android 上的嵌入页只允许麦克风,摄像头会被拒绝。需要拍摄时请使用独立页或原生 SDK。

文件选择 走系统 / WebKit 文件选择器,支持单选与多选。

下载 WebView 下载与页面内下载都会回调给宿主,由宿主执行后再回传状态。

返回 先交给页面(约 300ms,用于关闭预览等)。页面不处理再询问宿主;宿主也不处理才关闭容器。


常见问题​

为什么打开失败? 检查地址是否为 HTTPS,以及 query 的 key 是否合法。iOS 还会校验 directChatId。

为什么清不掉站点数据? 会话仍在会返回 IN_USE。先关闭全部相关页面,等销毁完成后再清理。iOS 按 host 匹配 WebKit 记录,同一记录下的兄弟子域可能一并清除,但不会做全局清空。

嵌入的 Flutter 页面高度为 0? 给 VisitorView 明确高度,例如包在 Expanded 里。

如何直达某个会话? 设置 directChatId。

如何绑定已登录用户? 在 query 里传 sbs、sbs_mm、ranstr。不要传 visitor_id。

如何在 App 内显示关闭按钮? isApp = true。

主题如何跟随系统? 初始使用 theme = system。运行中只能切到 light 或 dark。

弱网、后台、锁屏、WebSocket? 请在真机上验证。SDK 不额外封装网络层。

是否支持截屏桥、图片剪贴板? 不提供截屏桥。图片剪贴板不保证所有系统 WebView 都能使用。