FCM 完整配置参数指南
1. Firebase 项目配置
1.1 创建 Firebase 项目
- 访问 Firebase Console
- 创建新项目或选择现有项目
- 添加 iOS 应用
1.2 下载配置文件
下载 GoogleService-Info.plist 文件,放置到:
1
ios/Runner/GoogleService-Info.plist
确保在 Xcode 中将此文件添加到 Runner 目标。
2. iOS APNs 配置(关键步骤)
2.1 生成 APNs 密钥
- 登录 Apple Developer
- 进入 Certificates, Identifiers & Profiles
- 选择 Keys → 点击 + 创建新密钥
- 勾选 Apple Push Notifications service (APNs)
- 下载
.p8密钥文件(只能下载一次,请妥善保存) - 记录 Key ID 和 Team ID
2.2 上传 APNs 密钥到 Firebase
- 在 Firebase Console 中选择你的项目
- 进入 项目设置 → Cloud Messaging 标签
- 找到 iOS 应用配置 部分
- 上传 APNs 密钥:
- APNs 身份验证密钥(.p8 文件)
- 密钥 ID
- Team ID
3. Xcode 项目配置
3.1 启用 Push Notifications
在 Xcode 中:
- 选择 Runner 项目
- 选择 Signing & Capabilities 标签
- 点击 + Capability
- 添加 Push Notifications
- 添加 Background Modes,勾选:
- Remote notifications
- Background fetch
3.2 配置 AppDelegate
编辑 ios/Runner/AppDelegate.swift:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import UIKit
import Flutter
import FirebaseCore
import FirebaseMessaging
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Firebase 初始化
FirebaseApp.configure()
// 设置 UNUserNotificationCenter 代理
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
}
// 设置 FCM 代理
Messaging.messaging().delegate = self
// 注册远程通知
application.registerForRemoteNotifications()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// 接收 APNs Token
override func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
// 处理注册失败
override func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("注册远程通知失败: \(error.localizedDescription)")
}
}
// FCM 代理扩展
extension AppDelegate: MessagingDelegate {
// FCM Token 刷新
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("FCM Token: \(fcmToken ?? "")")
// 将 token 发送到 Flutter 层
let dataDict: [String: String] = ["token": fcmToken ?? ""]
NotificationCenter.default.post(
name: Notification.Name("FCMToken"),
object: nil,
userInfo: dataDict
)
}
}
// 通知代理扩展
@available(iOS 10, *)
extension AppDelegate: UNUserNotificationCenterDelegate {
// 前台接收通知
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// 前台也显示通知
if #available(iOS 14.0, *) {
completionHandler([[.banner, .sound, .badge]])
} else {
completionHandler([[.alert, .sound, .badge]])
}
}
// 点击通知
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print("点击通知: \(userInfo)")
completionHandler()
}
}
3.3 Podfile 配置
编辑 ios/Podfile:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
platform :ios, '12.0'
# 如果使用 Firebase
pod 'Firebase/Messaging'
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# 确保 Firebase 正常工作
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
4. Flutter 配置参数
4.1 完整的 FCM Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
class FCMConfig {
// iOS 特定配置
static const bool iOSShowNotificationsInForeground = true;
static const bool iOSRequestCriticalAlert = false;
static const bool iOSRequestProvisional = false;
// 通知权限配置
static const NotificationSettings notificationSettings = NotificationSettings(
alert: AppleNotificationSetting.enabled,
announcement: AppleNotificationSetting.disabled,
authorizationStatus: AuthorizationStatus.authorized,
badge: AppleNotificationSetting.enabled,
carPlay: AppleNotificationSetting.disabled,
lockScreen: AppleNotificationSetting.enabled,
notificationCenter: AppleNotificationSetting.enabled,
showPreviews: AppleShowPreviewSetting.always,
timeSensitive: AppleNotificationSetting.enabled,
criticalAlert: AppleNotificationSetting.disabled,
sound: AppleNotificationSetting.enabled,
);
}
class FCMService {
static final FCMService _instance = FCMService._internal();
factory FCMService() => _instance;
FCMService._internal();
final FirebaseMessaging _fcm = FirebaseMessaging.instance;
String? _fcmToken;
String? get fcmToken => _fcmToken;
/// 初始化 FCM
Future<void> initialize() async {
try {
// 1. 请求通知权限
final settings = await _requestPermission();
if (settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional) {
// 2. 获取 FCM Token
await _getFCMToken();
// 3. 配置消息处理
_configureMessageHandlers();
// 4. 监听 Token 刷新
_listenToTokenRefresh();
// 5. iOS 前台通知配置
if (defaultTargetPlatform == TargetPlatform.iOS) {
await _fcm.setForegroundNotificationPresentationOptions(
alert: true, // 显示通知
badge: true, // 更新角标
sound: true, // 播放声音
);
}
print('✅ FCM 初始化成功');
} else {
print('❌ 用户拒绝通知权限');
}
} catch (e) {
print('❌ FCM 初始化失败: $e');
}
}
/// 请求通知权限
Future<NotificationSettings> _requestPermission() async {
return await _fcm.requestPermission(
alert: true, // 显示通知
announcement: false, // Siri 播报
badge: true, // 应用角标
carPlay: false, // CarPlay
criticalAlert: FCMConfig.iOSRequestCriticalAlert, // 重要通知
provisional: FCMConfig.iOSRequestProvisional, // 临时授权
sound: true, // 声音
);
}
/// 获取 FCM Token
Future<void> _getFCMToken() async {
try {
// iOS 模拟器可能返回 null
_fcmToken = await _fcm.getToken();
if (_fcmToken != null) {
print('📱 FCM Token: $_fcmToken');
// TODO: 将 token 发送到你的服务器
await _sendTokenToServer(_fcmToken!);
} else {
print('⚠️ 无法获取 FCM Token (可能是模拟器)');
}
} catch (e) {
print('❌ 获取 FCM Token 失败: $e');
}
}
/// 配置消息处理器
void _configureMessageHandlers() {
// 前台消息
FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
// 后台点击通知打开 APP
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessageOpenedApp);
// 检查启动消息
_checkInitialMessage();
}
/// 前台消息处理
Future<void> _handleForegroundMessage(RemoteMessage message) async {
print('📨 前台收到消息:');
print(' 标题: ${message.notification?.title}');
print(' 内容: ${message.notification?.body}');
print(' 数据: ${message.data}');
// iOS 会自动显示通知(如果配置了 setForegroundNotificationPresentationOptions)
}
/// 点击通知打开 APP
Future<void> _handleMessageOpenedApp(RemoteMessage message) async {
print('🔔 点击通知打开 APP:');
print(' 数据: ${message.data}');
// TODO: 处理导航逻辑
_navigateToPage(message.data);
}
/// 检查启动消息
Future<void> _checkInitialMessage() async {
RemoteMessage? initialMessage = await _fcm.getInitialMessage();
if (initialMessage != null) {
print('🚀 从通知启动 APP:');
print(' 数据: ${initialMessage.data}');
// TODO: 处理导航逻辑
_navigateToPage(initialMessage.data);
}
}
/// 监听 Token 刷新
void _listenToTokenRefresh() {
_fcm.onTokenRefresh.listen((newToken) {
print('🔄 FCM Token 刷新: $newToken');
_fcmToken = newToken;
// TODO: 更新服务器上的 token
_sendTokenToServer(newToken);
});
}
/// 发送 Token 到服务器
Future<void> _sendTokenToServer(String token) async {
// TODO: 实现你的 API 调用
print('📤 发送 Token 到服务器: $token');
}
/// 导航到指定页面
void _navigateToPage(Map<String, dynamic> data) {
// TODO: 根据 data 中的信息进行页面跳转
if (data.containsKey('type')) {
String type = data['type'];
print('导航到: $type');
}
}
/// 订阅主题
Future<void> subscribeToTopic(String topic) async {
await _fcm.subscribeToTopic(topic);
print('✅ 订阅主题: $topic');
}
/// 取消订阅主题
Future<void> unsubscribeFromTopic(String topic) async {
await _fcm.unsubscribeFromTopic(topic);
print('❌ 取消订阅主题: $topic');
}
/// 删除 Token
Future<void> deleteToken() async {
await _fcm.deleteToken();
_fcmToken = null;
print('🗑️ 已删除 FCM Token');
}
/// 获取通知权限状态
Future<AuthorizationStatus> getAuthorizationStatus() async {
final settings = await _fcm.getNotificationSettings();
return settings.authorizationStatus;
}
}
4.2 在 main.dart 中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
// 后台消息处理器(顶级函数)
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('🔔 后台消息: ${message.messageId}');
print(' 标题: ${message.notification?.title}');
print(' 数据: ${message.data}');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化 Firebase
await Firebase.initializeApp();
// 注册后台消息处理器
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
_initializeFCM();
}
Future<void> _initializeFCM() async {
await FCMService().initialize();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FCM Demo',
home: HomePage(),
);
}
}
5. 服务端推送消息配置
5.1 HTTP v1 API 格式(推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"message": {
"token": "设备的 FCM Token",
"notification": {
"title": "消息标题",
"body": "消息内容"
},
"data": {
"type": "chat",
"userId": "123",
"messageId": "456"
},
"apns": {
"payload": {
"aps": {
"alert": {
"title": "消息标题",
"body": "消息内容"
},
"badge": 1,
"sound": "default",
"content-available": 1
}
},
"headers": {
"apns-priority": "10",
"apns-push-type": "alert"
}
}
}
}
5.2 Legacy API 格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"to": "设备的 FCM Token",
"notification": {
"title": "消息标题",
"body": "消息内容",
"sound": "default",
"badge": "1"
},
"data": {
"type": "chat",
"userId": "123"
},
"priority": "high",
"content_available": true
}
6. 测试清单
- 真机测试(模拟器不支持推送)
- APP 前台接收通知
- APP 后台接收通知
- APP 完全关闭接收通知
- 点击通知跳转
- Token 获取和刷新
- 权限请求流程
完成以上配置后,FCM 推送应该可以正常工作了!