使用 Firebase Cloud Messaging 为 Flutter 应用发送和接收通知

1. 简介

上次更新日期:2022-04-04

此 Codelab 将引导您使用 Flutter 开发一款支持 Firebase Cloud Messaging (FCM) 的多平台应用。您将编写应用的一个实现部分,然后在 Android、iOS 和 Web 这三个平台上无缝构建和运行该应用。您还将学习如何在 Flutter 中集成 FCM,以及如何编写代码来接收和发送消息。最后,此 Codelab 介绍了 FCM HTTP v1 API 的特定于平台的块功能,该功能可让您发送一条在不同平台上具有不同行为的消息。

前提条件

对 Flutter 有基本的了解。

学习内容

  • 如何设置和创建 Flutter 应用。
  • 如何添加 FCM 依赖项。
  • 如何向应用发送单条 FCM 消息。
  • 如何向应用发送主题 FCM 消息。

所需条件

  • 配置了 Dart 和 Flutter 插件的最新稳定版 Android Studio

您可以使用以下任意设备运行本 Codelab:

(可选)如需使用 iOS 平台运行此 Codelab,您需要一部 iOS 设备、一个 Apple 开发者账号和一部安装了 Xcode 的 macOS 设备。

2. Flutter 设置

如果您已设置 Flutter 开发环境,请跳过此部分。

如需设置 Flutter 开发环境,请按以下步骤操作:

  1. 下载并安装适用于您的操作系统的 Flutter:安装 | Flutter
  2. 确保 Flutter 工具已添加到您的路径中。
  3. 按照设置编辑器 | Flutter 中的说明设置 Flutter 编辑器。请务必为编辑器安装 Flutter 和 Dart 插件。在此 Codelab 的其余部分,您将使用 Android Studio。
  4. 在命令行中,运行 flutter doctor,该命令会扫描您的设置并列出需要修复的任何缺失的依赖项。请按照说明修复任何重要的缺失依赖项。请注意,某些依赖项可能不是必需的。例如,如果您不打算开发 iOS 应用,那么缺少 CocoaPods 依赖项就不会成为阻碍性问题。
  5. 运行此命令可在 fcmflutter 目录 flutter create --org com.flutter.fcm --project-name fcmflutter fcmflutter 中创建 Flutter 应用,然后将目录更改为 fcmflutter
  1. 在 Android Studio 中,依次前往 File -> Open,找到 Flutter 应用的路径,然后点击 Open 以在 Android Studio 中打开项目。应用代码位于文件 lib/main.dart 中。

在 Android Studio 工具栏中,点击下拉箭头以选择 Android 设备。如果目标选择器为空,请安装虚拟 Android 设备Chrome 浏览器或 iOS 模拟器(如果您希望从 Web 浏览器或 iOS 设备启动应用)。您可能需要手动启动设备并刷新列表,才能找到目标设备。

Android Studio 工具栏,其中突出显示了 build 目标菜单下拉箭头。

点击 Run Android Studio 中的“运行”按钮 以启动应用。

已启动的 Flutter 演示应用的界面

恭喜!您已成功创建 Flutter 应用。

3. Firebase 和 FlutterFire 设置

如需开发与 Firebase Cloud Messaging 集成的 Flutter 应用,您需要:

  • Firebase 项目。
  • 可正常运行的 Firebase CLI。
  • FlutterFire 的安装。
  • 使用 flutterfire configure 配置和生成的应用。

创建 Firebase 项目

如果您已有 Firebase 项目,则可以跳过此步骤。

  1. 使用您的 Google 账号登录 Firebase 控制台
  2. 点击相应按钮以创建新项目,然后输入项目名称(例如 fcm4flutter)。
  3. 点击继续
  4. 如果看到相关提示,请查看并接受 Firebase 条款,然后点击继续
  5. (可选)在 Firebase 控制台中启用 AI 辅助功能(称为“Gemini in Firebase”)。
  6. 在此 Codelab 中,您不需要使用 Google Analytics,因此请关闭 Google Analytics 选项。
  7. 点击创建项目,等待项目完成预配,然后点击继续

恭喜!您已成功创建 Firebase 项目。

设置 Firebase CLI

如果您已设置 Firebase CLI,则可以跳过此步骤。

前往 Firebase CLI 参考文档下载并安装 Firebase CLI。运行以下命令,使用您的 Google 账号登录 Firebase:

firebase login

设置 FlutterFire

  1. 使用以下命令安装 FlutterFire 插件:flutter pub add firebase_core
  2. 安装 FCM 插件:flutter pub add firebase_messaging
  3. 设置 FlutterFire CLI:dart pub global activate flutterfire_cli
  4. 在 Flutter 上配置 Firebase 项目:flutterfire configure --project=fcm4flutter. 使用箭头键和空格键选择平台,或按 Enter 键使用默认平台。

此 Codelab 使用默认平台(Android、iOS 和 Web),但您也可以仅选择一个或两个平台。如果系统提示您输入 iOS 软件包 ID,请输入 com.flutter.fcm.fcmflutter 或您自己的 iOS 软件包 ID(格式为 [company domain name].[project name])。命令完成后,刷新 Firebase 控制台页面。您会看到,它已在 Firebase 项目下为所选平台创建了应用。

Firebase 控制台的裁剪屏幕截图,显示了为所选平台创建的应用

此命令会在 lib 目录下生成一个 firebase_options.dart 文件,其中包含初始化所需的所有选项。

为 iOS 设置 Cloud Messaging

  1. 前往 Apple 开发者页面,然后在密钥标签页上点击创建密钥

Apple 开发者页面的裁剪屏幕截图,突出显示了密钥创建的页面组件

  1. 输入密钥的名称,然后选中 Apple 推送通知服务 (APNs)一张经过裁剪的 Apple 开发者页面屏幕截图,其中突出显示了新密钥名称的文本框
  2. 下载密钥文件,该文件的扩展名为 .p8一张经过裁剪的 Apple 开发者页面屏幕截图,其中突出显示了用于下载密钥的按钮
  3. Firebase 控制台中,前往项目的项目设置,然后选择 Cloud Messaging 标签页。

Firebase 控制台页面的局部屏幕截图,突出显示了用于更新项目设置的组件

Firebase 控制台页面的局部屏幕截图,突出显示了“Cloud Messaging”标签页

  1. Cloud Messaging 标签页中,上传 iOS 应用的 APNs 密钥文件。输入云消息传递标签页中的 APNs 密钥 ID 和团队 ID(可在 Apple 会员中心找到)。一张经过裁剪的 Firebase 控制台页面屏幕截图,突出显示了用于上传 APNs 身份验证密钥的按钮

4. FCM 准备工作

应用需要先执行以下操作,然后才能接收来自 FCM 的消息:

  • 初始化 FlutterFire。
  • 请求通知权限。
  • 向 FCM 注册以获取注册令牌。

初始化

如需初始化服务,请将 main 函数 (lib/main.dart) 替换为以下代码:

// core Flutter primitives
import 'package:flutter/foundation.dart';
// core FlutterFire dependency
import 'package:firebase_core/firebase_core.dart';
// generated by 
flutterfire configure
import 'firebase_options.dart';
// FlutterFire's Firebase Cloud Messaging plugin
import 'package:firebase_messaging/firebase_messaging.dart';

// TODO: Add stream controller
// TODO: Define the background message handler

Future<void> main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(
   options: DefaultFirebaseOptions.currentPlatform,
 );

 // TODO: Request permission
 // TODO: Register with FCM
 // TODO: Set up foreground message handler
 // TODO: Set up background message handler

 runApp(MyApp());
}

然后在 Android Studio 中运行 Tools -> Flutter -> Flutter Pub Get,以加载在设置 FlutterFire 中添加的软件包,并在 Android Studio 中显示具有相应 Intellisense 设置的代码。

这会为当前平台 DefaultFirebaseOptions.currentPlatform 初始化 FlutterFire,该平台是从生成的 firebase_options.dart 文件导入的。请注意,initializeApp 是一个异步函数,而 await 关键字可确保在运行应用之前完成初始化。

请求权限

应用需要征得用户同意才能接收通知。firebase_messaging 提供的 requestPermission 方法会显示一个对话框或弹出式窗口,提示用户允许或拒绝权限。

首先,将此代码复制到注释 TODO: Request permission 下面的主函数中。返回的 settings 会告知您用户是否已授予权限。我们建议仅在用户需要使用需要访问权限的功能时才请求权限(例如,当用户在应用设置中开启通知时)。在此 Codelab 中,为简单起见,我们在应用启动时请求权限。

final messaging = FirebaseMessaging.instance;

final settings = await messaging.requestPermission(
 alert: true,
 announcement: false,
 badge: true,
 carPlay: false,
 criticalAlert: false,
 provisional: false,
 sound: true,
);

 if (kDebugMode) {
   print('Permission granted: ${settings.authorizationStatus}');
 }

接下来,在 Android Studio 工具栏中,从目标选择器中选择 Chrome (web),然后再次运行应用。

Android Studio 工具栏的局部屏幕截图,其中显示了目标选择器和“运行”按钮

然后,系统会启动一个 Chrome 标签页,其中包含一个请求权限的弹出式窗口。如果您点击 Allow,您会在 Android Studio 控制台中看到以下日志:Permission granted: AuthorizationStatus.authorized。允许或阻止权限请求后,您的回答会与应用一起存储在浏览器中,并且系统不会再次显示该弹出式窗口。请注意,当您在 Android Studio 上再次运行 Web 应用时,系统可能会再次提示您授予权限。一张 Chrome 标签页的剪裁屏幕截图,其中显示了一个弹出式窗口,询问用户是否要

注册

将此代码复制到注释 TODO: Register with FCM 下方的主函数中,以向 FCM 注册。getToken 调用会返回一个注册令牌,应用服务器或受信任的服务器环境可以使用该令牌向用户发送消息。

// It requests a registration token for sending messages to users from your App server or other trusted server environment.
String? token = await messaging.getToken();

if (kDebugMode) {
  print('Registration Token=$token');
}

在 Android Studio 工具栏中,选择一个 Android 设备并运行应用。在 Android Studio 控制台中,注册令牌会以如下格式输出:

I/flutter ( 3717): Permission granted: AuthorizationStatus.authorized
I/flutter ( 3717): Registration Token=dch. . . D2P:APA9. . .kbb4

将其复制到文本编辑器中,因为您稍后会使用它来发送消息。

uses-sdk:minSdkVersion 16 cannot be smaller than version 19 declared in library [:firebase_messaging]

在网页上接收消息的额外步骤

Web 应用需要执行两个额外的步骤才能获取注册令牌并监听传入的消息。Web 需要向 getToken 传递 VAPID 密钥,才能授权向支持的 Web 推送服务发送请求。

首先,在 Firebase 控制台中打开 Firebase 项目的 Cloud Messaging 标签页,向下滚动到 Web 配置部分,找到现有密钥对或生成新的密钥对。点击突出显示的按钮复制密钥,以便将其用作 vapidKey。

Web 配置页面的“Web 推送证书”组件的裁剪屏幕截图,突出显示了密钥对

接下来,将“注册”部分中的注册代码替换为以下代码,然后更新 vapidKey:

// TODO: replace with your own VAPID key
 const vapidKey = "<YOUR_PUBLIC_VAPID_KEY_HERE>";

 // use the registration token to send messages to users from your trusted server environment
 String? token;

 if (DefaultFirebaseOptions.currentPlatform == DefaultFirebaseOptions.web) {
   token = await messaging.getToken(
     vapidKey: vapidKey,
   );
 } else {
   token = await messaging.getToken();
 }

 if (kDebugMode) {
   print('Registration Token=$token');
 }

然后,在项目根目录中的 web/ 目录下创建一个 firebase-messaging-sw.js 文件。将以下内容复制到 firebase-messaging-sw.js,以允许 Web 应用接收 onMessage 事件。如需了解详情,请参阅在 Service Worker 中设置通知选项

importScripts("https://www.gstatic.com/firebasejs/9.6.10/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/9.6.10/firebase-messaging-compat.js");

// todo Copy/paste firebaseConfig from Firebase Console
const firebaseConfig = {
 apiKey: "...",
 authDomain: "...",
 databaseURL: "...",
 projectId: "...",
 storageBucket: "...",
 messagingSenderId: "...",
 appId: "...",
};

firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

// todo Set up background message handler

之后,在项目设置 -> 常规标签页下,向下滚动并找到 Web 应用,复制 firebaseConfig 代码部分并将其粘贴到 firebase-messaging-sw.js 中。Firebase 配置页面的 Web 应用组件的裁剪屏幕截图

最后,在 Android Studio 工具栏中,选择目标选择器中的 Chrome (web) 并运行应用。在 Android Studio 控制台中,注册令牌会像这样输出:

Debug service listening on ws://127.0.0.1:61538/BLQQ3Fg-h7I=/ws
Permission granted: AuthorizationStatus.authorized
Registration Token=fH. . .ue:APA91. . .qwt3chpv

将注册令牌复制到文本编辑器中,以便稍后使用该令牌发送消息。

在 iOS 上接收消息的额外步骤

如需接收来自 FCM 的消息,iOS 设备需要在 Xcode 上启用推送通知后台模式

  1. 在 Android Studio 中,右键点击项目名称,然后依次选择 Flutter -> 在 Xcode 中打开 iOS 模块裁剪后的屏幕截图
  2. Xcode 启动后,在项目目标的签名和功能标签页中启用推送通知后台模式。如需了解详情,请参阅配置应用
  3. 在 Android Studio 工具栏中,在目标选择器中选择 iOS 设备,然后运行应用。授予通知权限后,注册令牌会显示在 Android Studio 控制台中。

一张经过裁剪的屏幕截图,显示 iOS 应用请求发送通知的权限

恭喜,您已成功向 FCM 注册应用。您已准备好接收消息,如下一部分中所述。

5. 接收来自 FCM 的消息

设置消息处理程序

当应用处于前台模式时,需要处理 onMessage 事件;当应用处于后台时,需要处理 onBackgroundMessage 事件。

前台消息处理程序

首先,在文件 main.dart 中的注释 TODO: Add stream controller 之后添加一个流控制器,以便将消息从事件处理脚本传递到界面。

import 'package:rxdart/rxdart.dart';
// used to pass messages from event handler to the UI
final _messageStreamController = BehaviorSubject<RemoteMessage>();

如需添加依赖项 rxdart,请从项目目录中运行以下命令:flutter pub add rxdart

接下来,在 Android Studio 中运行 Tools -> Flutter -> Flutter Pub Get,以加载 rxdart.dart 软件包并在 Android Studio 中显示具有相应 Intellisense 设置的代码。

然后,添加一个事件处理脚本,以在注释 TODO: Set up foreground message handler 之后监听前台消息。它会打印日志并将消息发布到流控制器。

 FirebaseMessaging.onMessage.listen((RemoteMessage message) {
   if (kDebugMode) {
     print('Handling a foreground message: ${message.messageId}');
     print('Message data: ${message.data}');
     print('Message notification: ${message.notification?.title}');
     print('Message notification: ${message.notification?.body}');
   }

   _messageStreamController.sink.add(message);
 });

之后,将文件 main.dart 中的原始 State widget 替换为以下代码,该代码会在 State widget 中向流控制器添加订阅者,并在 widget 上显示最后一条消息。

class _MyHomePageState extends State<MyHomePage> {
 String _lastMessage = "";

 _MyHomePageState() {
   _messageStreamController.listen((message) {
     setState(() {
       if (message.notification != null) {
         _lastMessage = 'Received a notification message:'
             '\nTitle=${message.notification?.title},'
             '\nBody=${message.notification?.body},'
             '\nData=${message.data}';
       } else {
         _lastMessage = 'Received a data message: ${message.data}';
       }
     });
   });
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text(widget.title),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: <Widget>[
           Text('Last message from Firebase Messaging:',
               style: Theme.of(context).textTheme.titleLarge),
           Text(_lastMessage, style: Theme.of(context).textTheme.bodyLarge),
         ],
       ),
     ),
   );
 }
}

Android/iOS 的后台消息处理程序

当应用在后台运行时,消息由 onBackgroundMessage 处理程序处理。处理程序应为顶级函数。当应用被带到前台时,可以通过处理消息(请参阅处理互动)或与应用服务器同步来更新界面。

在主函数外部的注释 TODO: Define the background message handler 后面创建处理函数,并在主函数中的注释 TODO: Set up background message handler 后面调用该函数。

// TODO: Define the background message handler
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
 await Firebase.initializeApp();

 if (kDebugMode) {
   print("Handling a background message: ${message.messageId}");
   print('Message data: ${message.data}');
   print('Message notification: ${message.notification?.title}');
   print('Message notification: ${message.notification?.body}');
 }
}

void main() {
 ...

 // TODO: Set up background message handler
 FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

 runApp(MyApp());
}

网页的后台消息处理程序

自 FlutterFire firebase_messaging 版本 11.2.8 起,在基于 Web 的平台上处理后台消息需要不同的流程。因此,您需要在 service worker web/firebase-messaging-sw.js 中添加单独的消息处理程序。

messaging.onBackgroundMessage((message) => {
 console.log("onBackgroundMessage", message);
});

设置应用服务器

  1. 在 Android Studio 中打开 https://github.com/FirebaseExtended/firebase_fcm_flutter/tree/main/server 项目,以导入起始服务器代码。该服务器是一个基于 Gradle 的 Java 项目,依赖于 firebase-admin SDK,后者提供 FCM 消息发送功能。
  2. 设置一个 Firebase 服务账号,以便 Firebase Admin SDK 授权对 FCM API 的调用。在 Firebase 控制台中打开项目设置,然后选择服务账号标签页。选择“Java”,然后点击 Generate new private key 下载配置代码段。一张经过裁剪的屏幕截图,突出显示了“项目设置”页面中“服务账号”组件的 Admin SDK 配置代码段
  3. 将该文件重命名为 service-account.json,并将其复制到服务器项目的 src/main/resources 路径。

发送测试消息

FcmSender.java 文件中,sendMessageToFcmRegistrationToken 会撰写包含数据载荷的通知消息。注册令牌用于指定消息发送到的应用实例。

private static void sendMessageToFcmRegistrationToken() throws Exception {
   String registrationToken = "REPLACE_WITH_FCM_REGISTRATION_TOKEN";
   Message message =
       Message.builder()
           .putData("FCM", "https://firebase.google.com/docs/cloud-messaging")
           .putData("flutter", "https://flutter.dev/")
           .setNotification(
               Notification.builder()
                   .setTitle("Try this new app")
                   .setBody("Learn how FCM works with Flutter")
                   .build())
           .setToken(registrationToken)
           .build();

   FirebaseMessaging.getInstance().send(message);

   System.out.println("Message to FCM Registration Token sent successfully!!");
 }
  1. 复制从“注册”部分复制的 Android 注册令牌,然后将其粘贴到变量 registrationToken 的值中。
  2. 点击 Run Android Studio 中的“运行”按钮 运行主函数,并通过 FCM 将消息发送给用户。一张经过裁剪的屏幕截图,显示了 Android Studio 中 FcmSender.java 主函数旁边的“运行”图标

当 Android 应用在后台运行时,消息会显示在通知栏中。

Android 通知栏中显示的消息的剪裁屏幕截图

当 Android 应用在前台运行时,您会在 Android Studio 控制台中看到一条日志:“Handling a foreground message”。由于界面订阅了新消息的流控制器,因此消息内容也会显示在界面中。

Android 应用中显示的消息内容的裁剪屏幕截图

如果您粘贴注册令牌并从应用服务器或其他受信任的服务器环境发送消息,您会看到类似的行为:

  • 当 Web 应用在后台运行时(即,当它被另一个窗口隐藏或另一个标签页处于活动状态时),您会看到一条 Web 通知。

Chrome 浏览器中显示的网页通知的裁剪屏幕截图

  • 当 Web 应用位于前台时,您可以在 Chrome 控制台中查看日志,方法是右键点击网页,然后选择 Inspect。消息内容也会显示在界面中。Chrome 控制台的截图(已裁剪),其中包含调试日志

6. 发送主题消息

FCM HTTP v1 API 的平台替换功能可让消息发送请求在不同平台上具有不同的行为。此功能的一个使用场景是根据平台显示不同的通知消息内容。当您使用主题消息传递功能定位多台设备(可能跨多个平台)时,此功能可发挥最大效用。本部分将逐步介绍如何让应用接收针对每个平台自定义的主题消息。

从客户端订阅主题

如需订阅主题,请在 Flutter 应用的 main.dart 文件中调用 main 函数末尾的 messaging.subscribeToTopic 方法。

// subscribe to a topic.
const topic = 'app_promotion';
await messaging.subscribeToTopic(topic);

[可选] 从服务器为 Web 订阅主题

如果您不是在 Web 平台上进行开发,则可以跳过此部分。

FCM JS SDK 目前不支持客户端主题订阅。不过,您可以使用 Admin SDK 的服务器端主题管理 API 进行订阅。此代码展示了如何使用 Java Admin SDK 在服务器端订阅主题。

 private static void subscribeFcmRegistrationTokensToTopic() throws Exception {
   List<String> registrationTokens =
       Arrays.asList(
           "REPLACE_WITH_FCM_REGISTRATION_TOKEN"); // TODO: add FCM Registration Tokens to
   // subscribe
   String topicName = "app_promotion";

   TopicManagementResponse response =     FirebaseMessaging.getInstance().subscribeToTopic(registrationTokens, topicName);
   System.out.printf("Num tokens successfully subscribed %d", response.getSuccessCount());
 }

打开应用服务器,然后点击运行 图标 Android Studio 中的“运行”按钮 以运行 FcmSubscriptionManager.java 文件中的 main 函数:

Android Studio 中 FcmSubscriptionManager.java 主函数旁显示的“运行”图标的裁剪屏幕截图

向主题发送包含平台替换项的消息

现在,您可以发送主题平台替换消息了。在以下代码段中:

  • 您构建了一个发送请求,其中包含基本消息和标题“A new app is available”。
  • 该消息会在 iOS 和 Web 平台上生成标题为“A new app is available”的显示通知。
  • 该消息会在 Android 设备上生成标题为“A new Android app is available”的显示通知。
private static void sendMessageToFcmTopic() throws Exception {
   String topicName = "app_promotion";

   Message message =
       Message.builder()
           .setNotification(
               Notification.builder()
                   .setTitle("A new app is available")
                   .setBody("Check out our latest app in the app store.")
                   .build())
           .setAndroidConfig(
               AndroidConfig.builder()
                   .setNotification(
                       AndroidNotification.builder()
                           .setTitle("A new Android app is available")
                           .setBody("Our latest app is available on Google Play store")
                           .build())
                   .build())
           .setTopic("app_promotion")
           .build();

   FirebaseMessaging.getInstance().send(message);

   System.out.println("Message to topic sent successfully!!");
 }

FcmSender.java 文件的 main 函数中,取消对 sendMessageToFcmTopic(); 的注释。点击 Run Android Studio 中的“运行”按钮 以发送主题消息。

7. 总结与后续步骤

总而言之,您已了解如何使用 Flutter 和 FCM 进行富有吸引力的多平台应用开发,包括环境设置、依赖项集成以及消息接收和发送。如需深入了解,请参阅以下资料:

Codelab

References