fcm_utils.dart 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:firebase_messaging/firebase_messaging.dart';
  4. import 'package:flutter_local_notifications/flutter_local_notifications.dart';
  5. import 'package:router/componentRouter/component_service_manager.dart';
  6. import 'package:shared/utils/log_utils.dart';
  7. // 定义一个回调函数类型
  8. typedef TokenCallback = void Function(String token);
  9. class FcmUtils {
  10. //FCM SDK对象
  11. final _firebaseMessaging = FirebaseMessaging.instance;
  12. //Notifications插件对象
  13. final _localNotifications = FlutterLocalNotificationsPlugin();
  14. //Android 8+ 的消息通道
  15. final _androidChannel = const AndroidNotificationChannel(
  16. 'Normal', //设置 Channel ID
  17. 'Channel Normal Android', //设置 Channel 名称
  18. importance: Importance.defaultImportance); //设置该 Channel 的优先级
  19. // 初始化,获取设备token
  20. Future<void> initNotifications({
  21. TokenCallback? onToken, // Token回调函数
  22. }) async {
  23. //申请动态通知权限
  24. await _firebaseMessaging.requestPermission();
  25. //获取 FCM Token
  26. final fCMToken = await _firebaseMessaging.getToken();
  27. if (fCMToken != null) {
  28. Log.d("FCM -> Token:$fCMToken");
  29. onToken?.call(fCMToken); // 将初始 Token 通过回调传递
  30. }
  31. //刷新 FCM Token 的监听
  32. _firebaseMessaging.onTokenRefresh.listen((newToken) {
  33. Log.d("FCM onTokenRefresh -> Token:$newToken");
  34. onToken?.call(newToken); // 将刷新后的 Token 通过回调传递
  35. });
  36. //拿到 FCM Token 之后初始化一些监听
  37. _initPushNotifications();
  38. _initLocalNotifications();
  39. }
  40. /// Notifications 插件初始化,监听前台消息
  41. Future _initLocalNotifications() async {
  42. const iOS = DarwinInitializationSettings();
  43. // @drawable/ic_launcher是应用的图标,路径是:android/app/src/main/res/mipmap/ic_launcher.png
  44. const android = AndroidInitializationSettings('@mipmap/ic_launcher');
  45. // Notifications 插件只需要处理 Android 和 iOS 平台的配置
  46. const settings = InitializationSettings(android: android, iOS: iOS);
  47. // Notifications 插件初始化
  48. await _localNotifications.initialize(settings, onDidReceiveNotificationResponse: (NotificationResponse response) {
  49. // android 前台消息点击回调
  50. final message = RemoteMessage.fromMap(jsonDecode(response.payload!));
  51. Log.d(response);
  52. // 处理收到消息
  53. handleMessage(message);
  54. });
  55. //Android 8 以上会通过 Channel 创建对应的通知渠道
  56. final platform = _localNotifications.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
  57. await platform?.createNotificationChannel(_androidChannel);
  58. }
  59. /// FCM 初始化接收消息的各种回调
  60. Future _initPushNotifications() async {
  61. await _firebaseMessaging.setForegroundNotificationPresentationOptions(alert: true, badge: true, sound: true);
  62. // 打开app时,会执行该回调,获取消息(通常是程序终止时,点击消息打开app的回调)
  63. _firebaseMessaging.getInitialMessage().then(
  64. (RemoteMessage? message) {
  65. if (message == null) return; // 没有消息不执行后操作
  66. handleMessage(message);
  67. },
  68. );
  69. // 后台程序运行时,点击消息触发
  70. FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) => handleMessage(message));
  71. // 前台消息,android不会弹出通知框,所以需要 Notifications 插件自定义本地通知(iOS没有前台消息,iOS的前台消息和后台运行时一样的效果)
  72. FirebaseMessaging.onMessage.listen((message) {
  73. Log.d("前台收到的消息 -> message:${message.data}");
  74. final notification = message.notification;
  75. if (notification == null) return;
  76. if (Platform.isIOS) return;
  77. _localNotifications.show(
  78. notification.hashCode,
  79. notification.title,
  80. notification.body,
  81. NotificationDetails(
  82. android: AndroidNotificationDetails(
  83. _androidChannel.id,
  84. _androidChannel.name,
  85. icon: '@mipmap/ic_launcher',
  86. )),
  87. payload: jsonEncode(message.toMap()));
  88. });
  89. // 后台处理,后台程序运行时收到消息,不打开app也会执行的回调
  90. // FirebaseMessaging.onBackgroundMessage(FirebaseApi.handleBackgroundMessage);
  91. }
  92. // 处理收到的消息,比如跳转页面或者其他处理
  93. void handleMessage(RemoteMessage message) {
  94. final dataJson = message.data;
  95. Log.d("点击消息的处理 handleMessage -> data:$dataJson");
  96. // 1、加入unit成功 ApprovedJoinUnitNotification
  97. // 2、加入unit失败 RejectedJoinUnitNotification
  98. // 3、online form批准了 ApprovedApplyOnlineFormNotification
  99. // 4、online form拒绝了 RejectedApplyOnlineFormNotification
  100. // 5、facility 预定成功了(不需要,因为支付了就是预定成功了)
  101. // 6、facility 预定失败了(长时间未支付,被系统取消了) FacilityBookingNotPaidCancelNotification
  102. // 7、每月物业费账单生成了
  103. // 8、每月停车费账单生成了
  104. // 9、服务订单长时间未支付 PaidServiceOrderNotPaidCancelNotification
  105. String? type = dataJson['type'];
  106. if (type == 'ApprovedApplyOnlineFormNotification' || type == 'RejectedApplyOnlineFormNotification') {
  107. //去 Form 详情
  108. ComponentServiceManager().formService.startFormDetailPage(dataJson['estate_online_form_id'], dataJson['id'], dataJson['online_form_type_id']);
  109. }
  110. }
  111. // static Future<void> handleBackgroundMessage(RemoteMessage message) async {
  112. // print('后台消息');
  113. // // BotToast.showText(text: '后台消息:${message.toString()}');
  114. // print('title:${message.notification?.title}');
  115. // print('body:${message.notification?.body}');
  116. // print('payload:${message.data}');
  117. // print('message:${message}');
  118. // }
  119. }