Flutter

最近更新时间:2026-06-22 15:20:29

我的收藏

前提条件

开始接入前,请先完成对应原生平台的推送前提配置:请参见 iOS 前提条件Android 前提条件

集成 TIMPush

Flutter 工程需要先引入推送插件,您可以在 pubspec.yaml 中添加依赖,也可以执行以下命令自动安装:
flutter pub add tencent_cloud_chat_push
完成 Flutter 插件集成后,还需要继续完成对应原生平台侧配置。
Android 端请参考 Android 集成 TIMPush 章节,完成各项配置;其中“集成 TIMPush 依赖”步骤与原生 Android 工程不同,请按下方 Flutter 方式处理。
android/app/build.gradleandroid/app/build.gradle.kts 按需引入目标厂商的 TIMPush 通道包。只有引入对应厂商包,才能启用该厂商的原生推送能力。VERSION 请前往 更新日志 获取并替换为实际版本号。
Groovy DSL
Kotlin DSL
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.pushdemo

import com.tencent.chat.flutter.push.tencent_cloud_chat_push.application.TencentCloudChatPushApplication

class MyApplication : TencentCloudChatPushApplication() {
override fun onCreate() {
super.onCreate()
}
}
然后在 android/app/src/main/AndroidManifest.xml<application> 标签中配置 android:name,指向上述自定义 Application 类。
<application
android:name=".MyApplication"
...>
</application>
iOS 端不需要执行原生 iOS 文档中“主 App target 集成”步骤,Flutter 插件会处理相关依赖。您只需要在 ios/Runner/AppDelegate.swift 中补充 TIMPush 相关配置,用于返回推送证书 ID、App Group ID,以及转发离线推送点击事件。
import UIKit
import Flutter

// Add these two import lines
import TIMPush
import 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
}
}

注册推送

registerPush 用于注册当前设备的推送 token。注册时机、参数含义和不同接入场景的说明,请参考 Android 注册推送iOS 注册推送 章节。
请注意,不要在 Flutter 程序入口的 main 方法中调用。建议在用户同意隐私政策后,并在业务侧合适时机调用。
App 冷启动注册 TIMPush(appKey 传 Push Key)
IM 登录后注册推送(appKey 传 null)
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() 获取当前设备推送标识。控制台或服务端发送测试消息时,可使用该值定位设备。
测试流程请参考 Android 测试推送iOS 测试推送文档。仅集成 TIMPush 的用户,建议优先使用腾讯云控制台接入测试能力验证离线推送。
如果您的项目已接入 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 参数,但该参数待废弃。新接入业务请不要在 registerPushonNotificationClicked 中处理跳转,统一使用 addPushListener
ext 是发送方写入的业务透传字段,建议使用 JSON 字符串,例如 {"conversationID":"user_A","conversationType":1}
若 App 冷启动后需要立即处理点击事件,建议尽早注册 listener,例如在登录流程完成后、业务首页初始化前完成注册。

配置消息分类(可选)

消息分类属于 Android 厂商通道策略,Flutter 侧没有额外的接入操作,请参考 Android 配置消息分类 文档,API 请参考 Flutter 消息分类 API 文档。

收不到推送排障流程

步骤一 原生侧排查

如果发送测试消息后设备没有收到推送,请先按平台文档 Android 收不到推送排障流程iOS 收不到推送排障流程 完成原生侧排查。

步骤二 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 中已设置 titledesc,并按业务需要设置 ext。如需点击跳转,ext 中应包含客户端可解析的页面或会话参数。

仍无法解决

如果按上述流程仍无法定位问题,请 联系我们 提交反馈。