前提条件
集成 TIMPush
Flutter 工程需要先引入推送插件,您可以在
pubspec.yaml 中添加依赖,也可以执行以下命令自动安装:flutter pub addtencent_cloud_chat_push
完成 Flutter 插件集成后,还需要继续完成对应原生平台侧配置。
在
android/app/build.gradle 或 android/app/build.gradle.kts 按需引入目标厂商的 TIMPush 通道包。只有引入对应厂商包,才能启用该厂商的原生推送能力。VERSION 请前往 更新日志 获取并替换为实际版本号。dependencies {implementation 'com.tencent.timpush:huawei:VERSION'implementation 'com.tencent.timpush:xiaomi:VERSION'implementation 'com.tencent.timpush:oppo:VERSION'implementation 'com.tencent.timpush:vivo:VERSION'implementation 'com.tencent.timpush:honor:VERSION'implementation 'com.tencent.timpush:meizu:VERSION'implementation 'com.tencent.timpush:fcm:VERSION'}
dependencies {implementation("com.tencent.timpush:huawei:VERSION")implementation("com.tencent.timpush:xiaomi:VERSION")implementation("com.tencent.timpush:oppo:VERSION")implementation("com.tencent.timpush:vivo:VERSION")implementation("com.tencent.timpush:honor:VERSION")implementation("com.tencent.timpush:meizu:VERSION")implementation("com.tencent.timpush:fcm:VERSION")}
Android 端还需要创建或复用自定义
Application 类,并继承 TencentCloudChatPushApplication。如果工程中已存在自定义 Application,直接改为继承该类,并确保 onCreate() 中调用 super.onCreate()。package com.example.pushdemoimport com.tencent.chat.flutter.push.tencent_cloud_chat_push.application.TencentCloudChatPushApplicationclass MyApplication : TencentCloudChatPushApplication() {override fun onCreate() {super.onCreate()}}
然后在
android/app/src/main/AndroidManifest.xml 的 <application> 标签中配置 android:name,指向上述自定义 Application 类。<applicationandroid:name=".MyApplication"...></application>
iOS 端不需要执行原生 iOS 文档中“主 App target 集成”步骤,Flutter 插件会处理相关依赖。您只需要在
ios/Runner/AppDelegate.swift 中补充 TIMPush 相关配置,用于返回推送证书 ID、App Group ID,以及转发离线推送点击事件。import UIKitimport Flutter// Add these two import linesimport TIMPushimport tencent_cloud_chat_push// Add `, TIMPushDelegate` to the following line@UIApplicationMain@objc class AppDelegate: FlutterAppDelegate, TIMPushDelegate {override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)return super.application(application, didFinishLaunchingWithOptions: launchOptions)}// Add this function@objc func businessID() -> Int32 {return TencentCloudChatPushFlutterModal.shared.businessID();}// Add this function@objc func applicationGroupID() -> String {return TencentCloudChatPushFlutterModal.shared.applicationGroupID()}// Add this function@objc func onRemoteNotificationReceived(_ notice: String?) -> Bool {TencentCloudChatPushPlugin.shared.tryNotifyDartOnNotificationClickEvent(notice)return true}}
注册推送
请注意,不要在 Flutter 程序入口的
main 方法中调用。建议在用户同意隐私政策后,并在业务侧合适时机调用。Future<void> registerTIMPush() async {final push = TencentCloudChatPush();final registerRes = await push.registerPush(// TODO: 替换为您的 SDKAppID。sdkAppId: 0,// TODO: 替换为 Push 客户端密钥。appKey: '<#YOUR_PUSH_APP_KEY#>',// iOS:替换为在腾讯云控制台生成的证书 ID;Android 不用传。apnsCertificateID: 0,// iOS 可选:如需统计推送触达,请替换为 Apple Developer Center 或 Xcode 中配置的 App Group ID;Android 不用传。// applicationGroupID: 'group.<#YOUR_APP_GROUP_ID#>',// 当前 Flutter API 仍要求传入该参数,但该回调待废弃。// 请不要在这里处理通知点击,统一使用 addPushListener。onNotificationClicked:({required String ext, String? userID, String? groupID}) {},);if (registerRes.code != 0) {debugPrint('registerPush failed: code=${registerRes.code}, ''msg=${registerRes.errorMessage}, deviceToken=${registerRes.data}',);return;}final ridRes = await push.getRegistrationID();debugPrint('registerPush success, registrationID=${ridRes.data}, ''code=${ridRes.code}, msg=${ridRes.errorMessage}',);}
Future<void> loginIMAndRegisterPush() async {final int sdkAppId = 0; // TODO: 替换为您的 SDKAppID。final String userID = '<#YOUR_USER_ID#>';final String userSig = '<#YOUR_USER_SIG#>';final initRes = await TencentImSDKPlugin.v2TIMManager.initSDK(sdkAppID: sdkAppId,);if (initRes.code != 0) {debugPrint('initSDK failed: code=${initRes.code}, desc=${initRes.desc}');return;}final loginRes = await TencentImSDKPlugin.v2TIMManager.login(userID: userID,userSig: userSig,);if (loginRes.code != 0) {debugPrint('login failed: code=${loginRes.code}, desc=${loginRes.desc}');return;}final push = TencentCloudChatPush();final registerRes = await push.registerPush(sdkAppId: sdkAppId,// iOS:替换为在腾讯云控制台生成的证书 ID;Android 不用传。apnsCertificateID: 0,// iOS 可选:如需统计推送触达,请替换为 Apple Developer Center 或 Xcode 中配置的 App Group ID;Android 不用传。// applicationGroupID: 'group.<#YOUR_APP_GROUP_ID#>',// 当前 Flutter API 仍要求传入该参数,但该回调待废弃。// 请不要在这里处理通知点击,后续章节会介绍推荐的监听方式。onNotificationClicked:({required String ext, String? userID, String? groupID}) {},);if (registerRes.code != 0) {debugPrint('registerPush failed: code=${registerRes.code}, ''msg=${registerRes.errorMessage}, deviceToken=${registerRes.data}',);return;}final ridRes = await push.getRegistrationID();debugPrint('registerPush after login success, registrationID=${ridRes.data}, ''code=${ridRes.code}, msg=${ridRes.errorMessage}',);}
配置消息触达统计(可选)
触达统计主要涉及厂商控制台、回执地址、APNs Notification Service Extension、App Group 等平台专属配置,请参考 Android 配置消息触达统计 和 iOS 配置消息触达统计 文档。
iOS 如需统计推送触达数据,请在
registerPush 时传入 applicationGroupID,并确认 ios/Runner/AppDelegate.swift 中已实现 applicationGroupID()。测试推送
运行 App 后,过滤
TIMPush 关键字查看注册日志。注册成功后,调用 getRegistrationID() 获取当前设备推送标识。控制台或服务端发送测试消息时,可使用该值定位设备。如果您的项目已接入 IM SDK,可通过 SDK API 发送一条带离线推送参数的消息进行验证。Flutter 侧示例如下:
Future<void> sendTestPushMessage({required String targetUserID,}) async {final createRes = await TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager.createTextMessage(text: 'Hello TIMPush');final message = createRes.data?.messageInfo;if (createRes.code != 0 || message == null) {debugPrint('createTextMessage failed: code=${createRes.code}');return;}final sendRes = await TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager.sendMessage(message: message,receiver: targetUserID,groupID: '',priority: MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,onlineUserOnly: false,offlinePushInfo: OfflinePushInfo(title: '推送标题',desc: '推送内容',ext: '{"action":"open_chat","conversationID":"c2c_$targetUserID"}',),);debugPrint('sendMessage result: code=${sendRes.code}, desc=${sendRes.desc}, ''msgID=${sendRes.data?.msgID}',);}
处理通知点击跳转
通知点击跳转完整流程请参考 Android 处理通知点击跳转 和 iOS 处理通知点击跳转 文档。Flutter 侧主要关注两处差异:通过 IM SDK API 发送消息时写入
ext,以及在客户端注册监听并解析 ext。如果您的项目已接入 IM SDK,可在发送消息时通过
OfflinePushInfo.ext 携带跳转参数。示例代码如下:Future<void> sendMessageWithPushExt({required String targetUserID,}) async {final createRes = await TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager.createTextMessage(text: 'Hello TIMPush');final message = createRes.data?.messageInfo;if (createRes.code != 0 || message == null) {debugPrint('createTextMessage failed: code=${createRes.code}');return;}final sendRes = await TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager.sendMessage(message: message,receiver: targetUserID,groupID: '',priority: MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,onlineUserOnly: false,offlinePushInfo: OfflinePushInfo(title: '推送标题',desc: '推送内容',ext: '{"conversationID":"$targetUserID","conversationType":1}',),);debugPrint('sendMessage result: code=${sendRes.code}, desc=${sendRes.desc}, ''msgID=${sendRes.data?.msgID}',);}
Flutter 客户端推荐使用
TencentCloudChatPush().addPushListener 监听通知点击事件,并在 onNotificationClicked 中解析 ext。示例代码如下:final TIMPushListener timPushListener = TIMPushListener(onRecvPushMessage: (TimPushMessage msg) {debugPrint('onRecvPushMessage: title=${msg.title}, desc=${msg.desc}, ''ext=${msg.ext}, msgID=${msg.messageID}',);},onRevokePushMessage: (String msgID) {debugPrint('onRevokePushMessage: msgID=$msgID');},onNotificationClicked: (String ext) {debugPrint('onNotificationClicked: ext=$ext');// 1. 解析 ext。JSON 结构由业务自定义,需与发送端约定一致。Map<String, dynamic>? extJson;try {extJson = jsonDecode(ext) as Map<String, dynamic>;} catch (e) {debugPrint('parse ext failed: $e');return;}final String? conversationID = extJson['conversationID'] as String?;final int? conversationType = extJson['conversationType'] as int?;if (conversationID == null || conversationType == null) {return;}// 2. TODO: 根据业务字段跳转到目标页面。// 若使用了 Chat / TUIKit,建议在用户登录成功后再跳转;// 冷启动场景可先把参数缓存起来,登录回调中再跳转。},);Future<void> addTIMPushListener() async {await TencentCloudChatPush().addPushListener(listener: timPushListener);}Future<void> removeTIMPushListener() async {await TencentCloudChatPush().removePushListener(listener: timPushListener);}
注意:
TencentCloudChatPush().registerPush 当前仍要求传入 onNotificationClicked 参数,但该参数待废弃。新接入业务请不要在 registerPush 的 onNotificationClicked 中处理跳转,统一使用 addPushListener。ext 是发送方写入的业务透传字段,建议使用 JSON 字符串,例如 {"conversationID":"user_A","conversationType":1}。若 App 冷启动后需要立即处理点击事件,建议尽早注册
listener,例如在登录流程完成后、业务首页初始化前完成注册。配置消息分类(可选)
收不到推送排障流程
步骤一 原生侧排查
步骤二 Flutter 侧可按以下顺序补充检查
1. 确认
registerPush 返回 code = 0。如果返回非 0,请先根据错误码和 errorMessage 排查,不要只看 App 是否启动成功。2. 注册成功后调用
getRegistrationID(),确认返回值非空。控制台或服务端发送测试消息时,使用的 RegistrationID / userID 应与这里返回的值一致。3. 确认
registerPush 的调用时机正确。仅使用 Push 能力时,可在 App 完成必要初始化、用户同意隐私政策并获得通知权限后调用;Chat / TUIKit 离线推送场景下,应在 IM 登录成功后调用。4. 确认通知点击逻辑已通过
TencentCloudChatPush().addPushListener 注册。registerPush 中的 onNotificationClicked 参数当前仅作为必填空实现保留,不建议继续在其中处理跳转。5. 如果通过 Chat SDK 发送测试消息,确认
OfflinePushInfo 中已设置 title、desc,并按业务需要设置 ext。如需点击跳转,ext 中应包含客户端可解析的页面或会话参数。仍无法解决