本文基于 Flutter 3.24 编写。

FCM 架构概览(来源: https://firebase.google.com/docs/cloud-messaging/fcm-architecture)
Firebase Cloud Messaging(FCM)是一种可以免费、可靠地发送消息的跨平台消息服务。
要实现 FCM 消息收发,主要需要两个组件。
- 可信的发送环境,例如 Cloud Functions for Firebase 或应用服务器,负责撰写消息、选择接收对象并发送。
- Apple、Android 或 Web(JavaScript)客户端应用,通过各平台的传输服务接收消息。
第一个组件通常是后端服务器,第二个通常是 Flutter 应用。本文主要介绍第二个组件,也就是如何在 Flutter 应用中获取 FCM 令牌并接收推送通知。
- 输入自定义名称,选择 APNs。
- 点击 Configure,按用途选择所需的设置。
- 配置完成后,依次点击 Continue 和 Register,创建密钥。
完成后,就会看到如下所示的密钥。
- 选择 App IDs,在下一页选择 App,再点击 Continue。
- 输入描述和 Bundle ID,向下滚动并勾选 Push Notifications,然后依次点击 Continue 和 Register,完成注册。
Bundle ID 对应
/ios/Runner.xcodeproj/project.pbxproj 中的 PRODUCT_BUNDLE_IDENTIFIER 值,例如 com.example.promleeblog。
1.3. 在 Xcode 中添加 Capability 🔗
在 VS Code 中右键点击
/ios 目录,选择 Open in Xcode。
进入
Runner > Runner > Signing & Capabilities,点击
+ Capability,搜索并添加
Push Notifications。
接着在 Signing & Capabilities 页签的 Background Modes 中,按下图勾选选项。

Team ID 的位置
到这里,iOS 环境的配置就完成了。
在 /ios/Podfile 中按下例添加权限。如果已经添加,只需确认 PERMISSION_NOTIFICATIONS 这一项已取消注释。
Podfile 代码
/ios/Podfilepost_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
# You can remove unused permissions here
# for more information: https://github.com/BaseflowIT/flutter-permission-handler/blob/master/permission_handler/ios/Classes/PermissionHandlerEnums.h
# e.g. when you don't need camera permission, just add 'PERMISSION_CAMERA=0'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.calendar
# 'PERMISSION_EVENTS=1',
## dart: PermissionGroup.calendarFullAccess
# 'PERMISSION_EVENTS_FULL_ACCESS=1',
## dart: PermissionGroup.reminders
# 'PERMISSION_REMINDERS=1',
## dart: PermissionGroup.contacts
# 'PERMISSION_CONTACTS=1',
## dart: PermissionGroup.camera
# 'PERMISSION_CAMERA=1',
## dart: PermissionGroup.microphone
# 'PERMISSION_MICROPHONE=1',
## dart: PermissionGroup.speech
# 'PERMISSION_SPEECH_RECOGNIZER=1',
## dart: PermissionGroup.photos
# 'PERMISSION_PHOTOS=1',
## The 'PERMISSION_LOCATION' macro enables the `locationWhenInUse` and `locationAlways` permission. If
## the application only requires `locationWhenInUse`, only specify the `PERMISSION_LOCATION_WHENINUSE`
## macro.
##
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
# 'PERMISSION_LOCATION=1',
# 'PERMISSION_LOCATION_WHENINUSE=0',
## dart: PermissionGroup.notification
'PERMISSION_NOTIFICATIONS=1',
## dart: PermissionGroup.mediaLibrary
# 'PERMISSION_MEDIA_LIBRARY=1',
## dart: PermissionGroup.sensors
# 'PERMISSION_SENSORS=1',
## dart: PermissionGroup.bluetooth
# 'PERMISSION_BLUETOOTH=1',
## dart: PermissionGroup.appTrackingTransparency
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
## dart: PermissionGroup.criticalAlerts
# 'PERMISSION_CRITICAL_ALERTS=1',
## dart: PermissionGroup.criticalAlerts
# 'PERMISSION_ASSISTANT=1',
]
end
end
end
Android 只需要配置权限(Android 13,即 API 级别 33 及以上)。
/android/app/src/main/AndroidManifest.xml<manifest ...>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application ...>
...
</application>
</manifest>
3. 编写接收推送通知的 Flutter 代码 🔗
3.1. 安装 firebase_messaging 软件包 🔗
flutter pub add firebase_messaging
在需要请求通知权限的位置添加以下代码。
import 'package:firebase_messaging/firebase_messaging.dart';
// 적절한 위치에 선언
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
在入口文件 main.dart 中添加以下代码。
使用后台消息处理函数时,有几点需要留意。
- 不能使用匿名函数。
- 必须是顶层函数,例如不能是需要先初始化类的类方法。
- 如果使用 Flutter 3.3.0 或更高版本,必须在消息处理函数的声明正上方添加 @pragma('vm:entry-point') 注解,否则该函数可能在发布模式下被树摇优化移除。
/lib/main.dartimport 'package:firebase_messaging/firebase_messaging.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
在入口文件中添加以下监听器。
/lib/main.dartimport 'package:firebase_messaging/firebase_messaging.dart';
void main(){
// ...
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
runApp(MyApp());
}
FCM 令牌是 Flutter 应用与 FCM 服务器通信时使用的唯一标识符。Flutter 应用取得 FCM 令牌后,就可以接收推送通知。
import 'package:firebase_messaging/firebase_messaging.dart';
// 적절한 위치에 선언
final req = await FirebaseMessaging.instance.requestPermission(
alert: true,
badge: true,
sound: true,
);
final fcmToken = await FirebaseMessaging.instance.getToken();
if (req.authorizationStatus == AuthorizationStatus.authorized && fcmToken != null) {
print('FCM Token: $fcmToken');
} else {
print('FCM Token: null');
}
进入通知编辑页面后,输入标题和正文,再点击发送测试消息,即可向上面获取的 FCM 令牌发送推送通知。
apns token has not been set yet. please ensure the apns token is available by calling getapnstoken()
如果基本配置都已正确完成,却仍然收到这条错误,可能是 Flutter 未能自动获取 APNs 令牌。此时需要手动获取 APNs 令牌。
/lib/main.dartimport 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
FirebaseMessaging messaging = FirebaseMessaging.instance;
await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
messaging.getAPNSToken(); // 이 코드 추가
await messaging.getToken();
runApp(MyApp());
}
在 iOS 真机上安装并运行应用时,可能出现上述错误。这是因为在 iOS 上获取 FCM 令牌之前,需要先取得 APNs 令牌。解决办法是在应用启动后先获取 APNs 令牌,再获取 FCM 令牌。可以按上述方式修改代码。
使用 Firebase Cloud Messaging 可以方便地发送推送通知。如果希望用户点击推送后跳转到指定页面或执行特定操作,还需要额外实现,下一篇将介绍这部分内容。
- 使用 HTTP v1 API 发送推送通知。
- 点击推送通知时跳转到指定页面。
- 点击推送通知时执行指定操作。