Flutter集成Firebase Cloud Messaging(FCM)

3,693 阅读5分钟

Firebase Cloud Messaging

Firebase Cloud Messaging(FCM)是一种跨平台的消息传递解决方案,可让您可靠地免费发送消息。

使用FCM,您可以通知客户端应用程序可以同步新电子邮件或其他数据。您可以发送通知消息来推动用户的重新参与和保留。对于即时消息之类的用例,一条消息可以将最多4 KB的有效负载传输到客户端应用程序。

以下内容基于flutter 1.22.4版本

准备工作

在开始之前,该文档假定您能够创建(或具有现有的)Flutter项目,并且还具有使用中的Firebase项目。如果您不满足这些先决条件,请参考准备工作

安装

1.添加依赖项

dependencies:
  flutter:
    sdk: flutter
  firebase_core: "^0.5.3"
  firebase_messaging: "^8.0.0-dev.11"

2.下载依赖项

$ flutter pub get

3. ios集成

参考:firebase.flutter.dev/docs/messag…

4. android集成

如果您正在使用Flutter Android Embedding V2(Flutter版本> = 1.12),则Android不需要其他集成步骤。—— 比如我。

低版本请参考:Flutte接入firebase messaging

使用

在使用Firebase Cloud Messaging之前,您必须首先确保已初始化FlutterFire。

await Firebase.initializeApp();

根据设备状态,传入消息的处理方式有所不同。要了解这些情况以及如何将FCM集成到您自己的应用程序中,首先重要的是确定设备可以处于的各种状态:

状态描述
Foreground当应用程序打开时,且在前台显示时。
Background打开应用程序时,但是在后台(最小化)。当用户按下设备上的“主页”按钮,通过应用程序切换器切换到另一个应用程序或在其他选项卡(Web)上打开应用程序时,通常会发生这种情况。
Terminated设备锁定或应用程序未运行时。用户可以通过设备上的应用程序切换器用户界面“将其刷掉”或关闭标签页(web)来终止应用程序。
  • 不论哪种情况,该应用程序必须至少打开一次(以允许在FCM中注册,必须要能连接到谷歌)。

  • 在Android上,如果强行终止程序,那么是收不到推送的。

  • 在iOS上,如果用户从应用切换器上滑动了该应用程序,则必须再次手动将其重新打开,以使后台消息重新开始工作。

申请许可(ios,Mac和Web)

在iOS,macOS和Web上,必须先征询用户许可,然后才能在设备上接收FCM有效负载。

不需要在Android应用程序上请求权限。

代码:

FirebaseMessaging messaging = FirebaseMessaging.instance;

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

print('用户授予权限结果: ${settings.authorizationStatus}');

authorizationStatus属性可以返回一个值,该值可用于确定用户的总体决策:

  • authorized: 用户已授予权限。
  • denied: 用户拒绝了权限。
  • notDetermined: 用户尚未选择是否授予许可。
  • provisional:用户授予了临时权限(请参阅[临时权限](firebase.flutter.dev/docs/messag…

在Android上,authorizationStatus始终是authorized。

NotificationSettings上的其他属性返回在当前设备上是启用,禁用还是不支持特定权限。

有关更多信息,请查看Permissions文档。

处理消息

消息类型

根据应用程序的当前状态,传入的有效负载需要不同的实现来处理它们:

ForegroundBackgroundTerminated
NotificationonMessageonBackgroundMessageonBackgroundMessage
DataonMessageonBackgroundMessage (*see below*)onBackgroundMessage (*see below*)
Notification & DataonMessageonBackgroundMessageonBackgroundMessage

前台消息(Foreground)

默认情况下,在应用程序处于前台时到达的通知消息在Android和iOS上均不会显示可见的通知。但是,可以覆盖此行为:在前台收到推送时里用flutter_local_notifications来显示一条通知。

    ///前台消息
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      print('在前台收到消息!');

      if (message.notification != null) {
        RemoteNotification notification = message.notification;

        ///显示通知
        if (notification != null && notification.android != null) {
          FlutterLocalNotificationsPlugin().show(
              notification.hashCode,
              notification.title,
              notification.body,
              NotificationDetails(
                android: AndroidNotificationDetails(
                  channel.id,
                  channel.name,
                  channel.description,
									// TODO 向android添加适当的可绘制资源(在drawable下)
                  icon: 'launch_background',
                ),
              ),
              payload: message.data.toString());
        }
      }
    });

后台消息(Background)

当前,在Android / Apple和基于Web的平台上处理后台消息的过程有所不同。

在后台运行应用程序时处理消息有些不同。可以通过onBackgroundMessage处理程序处理消息。

收到隔离信息后,就会生成隔离信息(仅适用于Android,iOS / macOS不需要单独的隔离信息,APNS真牛逼),即使您的应用程序未运行,也可以处理消息。

关于后台消息处理的方法,需要牢记以下几点:

  • 它不能是匿名函数。
  • 它必须是顶层函数(例如,不是需要初始化的类方法)。
  static Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
    print("收到后台消息: ${message.messageId}");
  }  

...
///后台消息
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

收到后台消息的时候,运行长时间的繁重任务会影响设备性能,并可能导致操作系统终止该过程。如果任务运行时间超过30秒,则设备可能会自动终止该进程。

onMessageOpenedApp:当从后台状态打开应用程序时,该流会发送RemoteMessage。此时可以监听到用户对此种通知的点击行为:

    ///点击后台消息打开App
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print('从后台打开!');
    });

应用终止时点击通知打开App(Terminated)

onBackgroundMessage是无法监听到此种通知的行为的,但是可以接收到用户对此种通知的点击行为:

getInitialMessage():如果从终止状态打开应用程序,则将返回包含RemoteMessage的Future。一旦使用完毕,RemoteMessage将被删除。

    ///应用从终止状态打开
    var m = await FirebaseMessaging.instance.getInitialMessage();
    if (m != null) {
      print('应用从终止状态打开:${m?.notification?.title}');
    }

要展示通知必须包含nofitication字段

处理交互

由于通知是可见的提示,因此用户通常会与通知进行交互(点击通知)。 Android和iOS上的默认行为是打开应用程序。如果应用程序终止,它将启动;如果它在后台,它将被带到前台。

firebase-messaging软件包提供了两种方法来处理此交互(上文一提到,再着重介绍下):

  • getInitialMessage():如果从终止状态打开应用程序,则将返回包含RemoteMessage的Future。一旦使用完毕,RemoteMessage将被删除。
  • onMessageOpenedApp:当从后台状态打开应用程序时,该流会发送RemoteMessage。

建议同时处理两种情况。

    ///点击后台消息打开App
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print('从后台打开!');
    });

    ///应用从终止状态打开
    var m = await FirebaseMessaging.instance.getInitialMessage();
    if (m != null) {
      print('应用从终止状态打开:${m?.notification?.title}');
    }

以下是官方示例:

class Application extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Application();
}

class _Application extends State<Application> {
  @override
  void initState() async {
    super.initState();

    // Get any messages which caused the application to open from
    // a terminated state.
    RemoteMessage initialMessage =
        await FirebaseMessaging.instance.getInitialMessage();

    // If the message also contains a data property with a "type" of "chat",
    // navigate to a chat screen
    if (initialMessage?.data['type'] == 'chat') {
      Navigator.pushNamed(context, '/chat',
          arguments: ChatArguments(initialMessage));
    }

    // Also handle any interaction when the app is in the background via a
    // Stream listener
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      if (message.data['type'] == 'chat') {
        Navigator.pushNamed(context, '/chat',
          arguments: ChatArguments(message));
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text("...");
  }
}

处理交互的方式取决于您的应用程序设置,但是上面的示例显示了一个使用StatefulWidget的基本示例。

至于前台通知的点击,请查看flutter_local_notifications来设置.

主题

用户可以订阅一个主题,我们发推送的时候对某一主题发出推送,订阅了这个主题的用户就会收到。

起到一个发送给多个用户推送的作用。

主要是我没找到通过http取到发推送的时候如何对所有用户发送。

订阅主题:

要订阅设备,请使用主题名称调用subscribeToTopic方法:

await FirebaseMessaging.instance.subscribeToTopic('weather');

取消订阅主题

要取消订阅主题,请使用主题名称调用unsubscribeFromTopic方法:

await FirebaseMessaging.instance.unsubscribeFromTopic('weather');

此处的意思是主题名称为weather.

通过Http发推送

如果您无法使用Firebase Admin SDK,则Firebase还支持通过HTTP, POST请求将消息发送到设备:

Authorization:服务器密钥

POST	https://fcm.googleapis.com/fcm/send 	HTTP/1.1

//请求头
Content-Type: application/json
Authorization: key={服务器密钥}

//body
{
	"to": "{fcmtoken}",
	"notification": {
		"icon": "juno_icon",
		"title": "自己的土",
		"body": "自己的地🤮",
		"sound": "default",
		"badge": 1,
		"content_available": true
	},
	"data": {
		"title": "自己的土",
		"body": "自己的地",
		"id": 1,
		"type": 2,
		"route": "ceceapp://main/fortune"
	}
}

ios后台推送要添加content_available才能收到回调 相关文档

基于此写的示例项目已发布到github Android,ios 前后台的推送都已调通,多个手机测试过。