iOS 14+ 上的 Flutter FCM 9+
Posted
技术标签:
【中文标题】iOS 14+ 上的 Flutter FCM 9+【英文标题】:Flutter FCM 9+ on iOS 14+ 【发布时间】:2021-11-28 22:32:15 【问题描述】:如何实现 FCM 9+ 以在 ios 版本 14+ 上正常工作?
【问题讨论】:
虽然我们学习知识,但形式上正确的方法是自己写一个问题,然后用信息自我回答。这样,您就可以遵守准则。否则,您的问题可能会因为不是问题/不清楚而被关闭。 官僚作风是停止分发有用提示的好方法。但你是对的。谢谢你的建议。 嗯,这样看吧...如果有人喜欢,你可以在问题和回答中获得积分:) 【参考方案1】:我之前关于Flutter FCM 7 implementation 的回答很有帮助,因此我决定为新的 FCM 9+ 版本编写相同的说明,并在几分钟内展示如何在我们的 Flutter 应用中实现平滑的消息传递。
迁移到 null 安全和 FCM 版本 9+ (IOS 14+) 后情况看起来并没有好转。我们遇到了同样的问题,但使用了新的包装器:)。
下面描述的指令可以帮助实现 FCM 9+ 并提供一些代码示例。也许这些说明可以帮助某人并防止浪费时间。
XCode 设置
AppDelegate.swift
import UIKit
import Flutter
import Firebase
import FirebaseMessaging
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
if #available(iOS 10.0, *)
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
Info.plist
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>FirebaseScreenReportingEnabled</key>
<true/>
消息示例(可调用函数)
您的消息必须使用以下选项发送:
mutableContent: true,
contentAvailable: true,
apnsPushType: "background"
只是在可调用函数中使用的示例
exports.sendNotification = functions.https.onCall(
async (data) =>
console.log(data, "send notification");
var userTokens = [USERTOKEN1,USERTOKEN2,USERTOKEN3];
var payload =
notification:
title: '',
body: '',
image: '',
,
data:
type:'',
,
;
for (const [userToken,userUID] of Object.entries(userTokens))
admin.messaging().sendToDevice(userToken, payload,
mutableContent: true,
contentAvailable: true,
apnsPushType: "background"
);
return code: 100, message: "notifications send successfully";
);
Flutter 消息服务
import 'dart:convert' as convert;
import 'dart:io' show Platform;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_app_badger/flutter_app_badger.dart';
import 'package:octopoos/entities/notification.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:uuid/uuid.dart';
class MessagingService
final Box prefs = Hive.box('preferences');
final FirebaseMessaging fcm = FirebaseMessaging.instance;
static final instance = MessagingService._();
bool debug = true;
/// Private Singleton Instance
MessagingService._();
/// Set FCM Presentation Options
Future<void> setPresentationOptions() async
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
/// Check PUSH permissions for IOS
Future<bool> requestPermission(bool withDebug = true) async
NotificationSettings settings = await fcm.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
// if (withDebug) debugPrint('[ FCM ] Push: $settings.authorizationStatus');
bool authorized = settings.authorizationStatus == AuthorizationStatus.authorized;
return (Platform.isIOS && authorized || Platform.isandroid) ? true : false;
/// Initialize FCM stream service
Future<void> initializeFcm() async
final String? currentToken = await fcm.getToken();
final String storedToken = prefs.get('fcmToken', defaultValue: '');
/// Refresh Device token & resubscribe topics
if (currentToken != null && currentToken != storedToken)
prefs.put('fcmToken', currentToken);
/// resubscribeTopics();
if (debug)
debugPrint('[ FCM ] token: $currentToken');
debugPrint('[ FCM ] service initialized');
/// Store messages to Hive Storage
void store(RemoteMessage message) async
final FirebaseAuth auth = FirebaseAuth.instance;
final Map options = message.data['options'] != null && message.data['options'].runtimeType == String
? convert.json.decode(message.data['options'])
: message.data['options'];
final AppNotification notificationData = AppNotification(
id: const Uuid().v4(),
title: message.data['title'] ?? '',
body: message.data['body'] ?? '',
image: message.data['image'] ?? '',
type: message.data['type'] ?? 'notification',
options: options,
createdAt: DateTime.now().toString(),
);
late Box storage;
switch (message.data['type'])
default:
storage = Hive.box('notifications');
break;
try
String id = const Uuid().v4();
storage.put(id, notificationData.toMap());
updateAppBadge(id);
if (debug) debugPrint('Document $id created');
catch (error)
if (debug) debugPrint('Something wrong! $error');
/// Update app badge
Future<void> updateAppBadge(String id) async
final bool badgeIsAvailable = await FlutterAppBadger.isAppBadgeSupported();
if (badgeIsAvailable && id.isNotEmpty)
final int count = Hive.box('preferences').get('badgeCount', defaultValue: 0) + 1;
Hive.box('preferences').put('badgeCount', count);
FlutterAppBadger.updateBadgeCount(count);
/// Subscribe topic
Future<void> subscribeTopic(required String name) async
await fcm.subscribeToTopic(name);
/// Unsubscribe topic
Future<void> unsubscribeTopic(required String name) async
await fcm.unsubscribeFromTopic(name);
/// Resubscribe to topics
Future<int> resubscribeTopics() async
final List topics = prefs.get('topics', defaultValue: []);
if (topics.isNotEmpty)
for (String topic in topics)
subscribeTopic(name: topic);
return topics.length;
AppNotification 模型
class AppNotification
String id;
String title;
String body;
String image;
String type;
Map options;
String createdAt;
AppNotification(
this.id = '',
this.title = '',
this.body = '',
this.image = '',
this.type = 'notification',
this.options = const ,
this.createdAt = '',
);
AppNotification.fromMap(Map snapshot, this.id)
: title = snapshot['title'],
body = snapshot['body'],
image = snapshot['image'],
type = snapshot['type'] ?? 'notification',
options = snapshot['options'] ?? ,
createdAt = (DateTime.parse(snapshot['createdAt'])).toString();
Map<String, dynamic> toMap() =>
"id": id,
"title": title,
"body": body,
"image": image,
"type": type,
"options": options,
"createdAt": createdAt,
;
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:provider/provider.dart';
import 'package:octopoos/services/messaging.dart';
import 'package:timezone/data/latest.dart' as tz;
Future<void> fcm(RemoteMessage message) async
MessagingService.instance.store(message);
/// Show foreground Push notification
/// !!! Flutter Local Notification Plugin REQUIRED
await notificationsPlugin.show(
0,
message.data['title'],
message.data['body'],
NotificationDetails(android: androidChannelSpecifics, iOS: iOSChannelSpecifics),
);
Future<void> main() async
/// Init TimeZone
tz.initializeTimeZones();
/// Init Firebase Core Application
await Firebase.initializeApp();
/// FCM Permissions & Background Handler
MessagingService.instance.setPresentationOptions();
FirebaseMessaging.onBackgroundMessage(fcm);
runApp(
MultiProvider(
providers: kAppProviders,
child: App(),
),
);
app.dart
@override
void initState()
super.initState();
initFcmListeners();
Future<void> initFcmListeners() async
MessagingService.instance.initializeFcm();
FirebaseMessaging.instance.getInitialMessage().then((message)
if (message != null) _handleMessage(message);
);
FirebaseMessaging.onMessage.listen(_handleMessage);
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
void _handleMessage(RemoteMessage message)
MessagingService.instance.store(message);
就是这样。不要忘记在真正的 IOS 设备上进行测试。 FCM 不适用于 IOS 模拟器。
【讨论】:
以上是关于iOS 14+ 上的 Flutter FCM 9+的主要内容,如果未能解决你的问题,请参考以下文章
Flutter FCM iOS 问题 - 在检索 FCM 令牌之前未设置 APNS 设备令牌
Flutter / FCM 通知未到达 Codemagic iOS 版本