iOS 14+ 上的 Flutter FCM 9+
Flutter FCM 9+ on iOS 14+
如何实施 FCM 9+ 以在 IOS 版本 14+ 上正常工作?
我之前关于 Flutter FCM 7 implementation 的回答很有帮助,所以我决定为新的 FCM 9+ 版本编写相同的说明,并在几分钟内展示如何在我们的 Flutter 应用程序中实现流畅的消息传递。
迁移到零安全和 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 模拟器上运行。
下面是如何在flutter中实现FCM的全过程
首先按照以下步骤在 firebase 控制台上设置您的应用 link Add Firebase to your Flutter app
添加依赖
firebase_core: ^1.12.0
firebase_messaging: ^11.2.6
将配置添加到应用程序端。
Android
加入你的Application
class
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
// ...
@Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingBackgroundService.setPluginRegistrant(this);
}
@Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
// ...
}
FlutterFirebaseMessagingBackgroundService 调用应用程序 onCreate 方法的回调。
iOS 整合
iOS 遵循此文档 setup iOS or macOS with Firebase Cloud Messaging.
添加功能
这是您的 main.dart
文件并将整个代码替换为以下内容:
import 'dart:async';
import 'dart:convert';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_messaging_example/firebase_config.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'message.dart';
import 'message_list.dart';
import 'permissions.dart';
import 'token_monitor.dart';
/// Define a top-level named handler which background/terminated messages will
/// call.
///
/// To verify things are working, check out the native platform logs.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp(options: DefaultFirebaseConfig.platformOptions);
print('Handling a background message ${message.messageId}');
}
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: 'AIzaSyAHAsf51D0A407EklG1bs-5wA7EbyfNFg0',
appId: '1:448618578101:ios:0b11ed8263232715ac3efc',
messagingSenderId: '448618578101',
projectId: 'react-native-firebase-testing',
),
);
// Set the background messaging handler early on, as a named top-level function
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
if (!kIsWeb) {
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}
runApp(MessagingExampleApp());
}
/// Entry point for the example application.
class MessagingExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Messaging Example App',
theme: ThemeData.dark(),
routes: {
'/': (context) => Application(),
'/message': (context) => MessageView(),
},
);
}
}
// Crude counter to make messages unique
int _messageCount = 0;
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String? token) {
_messageCount++;
return jsonEncode({
'token': token,
'data': {
'via': 'FlutterFire Cloud Messaging!!!',
'count': _messageCount.toString(),
},
'notification': {
'title': 'Hello FlutterFire!',
'body': 'This notification (#$_messageCount) was created via FCM!',
},
});
}
/// Renders the example application.
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
String? _token;
@override
void initState() {
super.initState();
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage? message) {
if (message != null) {
Navigator.pushNamed(
context,
'/message',
arguments: MessageArguments(message, true),
);
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channel.description,
// TODO add a proper drawable resource to android, for now using
// one that already exists in example app.
icon: 'launch_background',
),
),
);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('A new onMessageOpenedApp event was published!');
Navigator.pushNamed(
context,
'/message',
arguments: MessageArguments(message, true),
);
});
}
Future<void> sendPushMessage() async {
if (_token == null) {
print('Unable to send FCM message, no token exists.');
return;
}
try {
await http.post(
Uri.parse('https://api.rnfirebase.io/messaging/send'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: constructFCMPayload(_token),
);
print('FCM request for device sent!');
} catch (e) {
print(e);
}
}
Future<void> onActionSelected(String value) async {
switch (value) {
case 'subscribe':
{
print(
'FlutterFire Messaging Example: Subscribing to topic "fcm_test".',
);
await FirebaseMessaging.instance.subscribeToTopic('fcm_test');
print(
'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.',
);
}
break;
case 'unsubscribe':
{
print(
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".',
);
await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test');
print(
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.',
);
}
break;
case 'get_apns_token':
{
if (defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
print('FlutterFire Messaging Example: Getting APNs token...');
String? token = await FirebaseMessaging.instance.getAPNSToken();
print('FlutterFire Messaging Example: Got APNs token: $token');
} else {
print(
'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.',
);
}
}
break;
default:
break;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Cloud Messaging'),
actions: <Widget>[
PopupMenuButton(
onSelected: onActionSelected,
itemBuilder: (BuildContext context) {
return [
const PopupMenuItem(
value: 'subscribe',
child: Text('Subscribe to topic'),
),
const PopupMenuItem(
value: 'unsubscribe',
child: Text('Unsubscribe to topic'),
),
const PopupMenuItem(
value: 'get_apns_token',
child: Text('Get APNs token (Apple only)'),
),
];
},
),
],
),
floatingActionButton: Builder(
builder: (context) => FloatingActionButton(
onPressed: sendPushMessage,
backgroundColor: Colors.white,
child: const Icon(Icons.send),
),
),
body: SingleChildScrollView(
child: Column(
children: [
MetaCard('Permissions', Permissions()),
MetaCard(
'FCM Token',
TokenMonitor((token) {
_token = token;
return token == null
? const CircularProgressIndicator()
: Text(token, style: const TextStyle(fontSize: 12));
}),
),
MetaCard('Message Stream', MessageList()),
],
),
),
);
}
}
/// UI Widget for displaying metadata.
class MetaCard extends StatelessWidget {
final String _title;
final Widget _children;
// ignore: public_member_api_docs
MetaCard(this._title, this._children);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Container(
margin: const EdgeInsets.only(bottom: 16),
child: Text(_title, style: const TextStyle(fontSize: 18)),
),
_children,
],
),
),
),
);
}
}
遵循此文档:Cloud Messaging
如何实施 FCM 9+ 以在 IOS 版本 14+ 上正常工作?
我之前关于 Flutter FCM 7 implementation 的回答很有帮助,所以我决定为新的 FCM 9+ 版本编写相同的说明,并在几分钟内展示如何在我们的 Flutter 应用程序中实现流畅的消息传递。
迁移到零安全和 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 模拟器上运行。
下面是如何在flutter中实现FCM的全过程
首先按照以下步骤在 firebase 控制台上设置您的应用 link Add Firebase to your Flutter app
添加依赖
firebase_core: ^1.12.0
firebase_messaging: ^11.2.6
将配置添加到应用程序端。
Android
加入你的Application
class
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
// ...
@Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingBackgroundService.setPluginRegistrant(this);
}
@Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
// ...
}
FlutterFirebaseMessagingBackgroundService 调用应用程序 onCreate 方法的回调。
iOS 整合
iOS 遵循此文档 setup iOS or macOS with Firebase Cloud Messaging.
添加功能
这是您的 main.dart
文件并将整个代码替换为以下内容:
import 'dart:async';
import 'dart:convert';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_messaging_example/firebase_config.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'message.dart';
import 'message_list.dart';
import 'permissions.dart';
import 'token_monitor.dart';
/// Define a top-level named handler which background/terminated messages will
/// call.
///
/// To verify things are working, check out the native platform logs.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp(options: DefaultFirebaseConfig.platformOptions);
print('Handling a background message ${message.messageId}');
}
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: 'AIzaSyAHAsf51D0A407EklG1bs-5wA7EbyfNFg0',
appId: '1:448618578101:ios:0b11ed8263232715ac3efc',
messagingSenderId: '448618578101',
projectId: 'react-native-firebase-testing',
),
);
// Set the background messaging handler early on, as a named top-level function
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
if (!kIsWeb) {
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}
runApp(MessagingExampleApp());
}
/// Entry point for the example application.
class MessagingExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Messaging Example App',
theme: ThemeData.dark(),
routes: {
'/': (context) => Application(),
'/message': (context) => MessageView(),
},
);
}
}
// Crude counter to make messages unique
int _messageCount = 0;
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String? token) {
_messageCount++;
return jsonEncode({
'token': token,
'data': {
'via': 'FlutterFire Cloud Messaging!!!',
'count': _messageCount.toString(),
},
'notification': {
'title': 'Hello FlutterFire!',
'body': 'This notification (#$_messageCount) was created via FCM!',
},
});
}
/// Renders the example application.
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
String? _token;
@override
void initState() {
super.initState();
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage? message) {
if (message != null) {
Navigator.pushNamed(
context,
'/message',
arguments: MessageArguments(message, true),
);
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channel.description,
// TODO add a proper drawable resource to android, for now using
// one that already exists in example app.
icon: 'launch_background',
),
),
);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('A new onMessageOpenedApp event was published!');
Navigator.pushNamed(
context,
'/message',
arguments: MessageArguments(message, true),
);
});
}
Future<void> sendPushMessage() async {
if (_token == null) {
print('Unable to send FCM message, no token exists.');
return;
}
try {
await http.post(
Uri.parse('https://api.rnfirebase.io/messaging/send'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: constructFCMPayload(_token),
);
print('FCM request for device sent!');
} catch (e) {
print(e);
}
}
Future<void> onActionSelected(String value) async {
switch (value) {
case 'subscribe':
{
print(
'FlutterFire Messaging Example: Subscribing to topic "fcm_test".',
);
await FirebaseMessaging.instance.subscribeToTopic('fcm_test');
print(
'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.',
);
}
break;
case 'unsubscribe':
{
print(
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".',
);
await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test');
print(
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.',
);
}
break;
case 'get_apns_token':
{
if (defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
print('FlutterFire Messaging Example: Getting APNs token...');
String? token = await FirebaseMessaging.instance.getAPNSToken();
print('FlutterFire Messaging Example: Got APNs token: $token');
} else {
print(
'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.',
);
}
}
break;
default:
break;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Cloud Messaging'),
actions: <Widget>[
PopupMenuButton(
onSelected: onActionSelected,
itemBuilder: (BuildContext context) {
return [
const PopupMenuItem(
value: 'subscribe',
child: Text('Subscribe to topic'),
),
const PopupMenuItem(
value: 'unsubscribe',
child: Text('Unsubscribe to topic'),
),
const PopupMenuItem(
value: 'get_apns_token',
child: Text('Get APNs token (Apple only)'),
),
];
},
),
],
),
floatingActionButton: Builder(
builder: (context) => FloatingActionButton(
onPressed: sendPushMessage,
backgroundColor: Colors.white,
child: const Icon(Icons.send),
),
),
body: SingleChildScrollView(
child: Column(
children: [
MetaCard('Permissions', Permissions()),
MetaCard(
'FCM Token',
TokenMonitor((token) {
_token = token;
return token == null
? const CircularProgressIndicator()
: Text(token, style: const TextStyle(fontSize: 12));
}),
),
MetaCard('Message Stream', MessageList()),
],
),
),
);
}
}
/// UI Widget for displaying metadata.
class MetaCard extends StatelessWidget {
final String _title;
final Widget _children;
// ignore: public_member_api_docs
MetaCard(this._title, this._children);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Container(
margin: const EdgeInsets.only(bottom: 16),
child: Text(_title, style: const TextStyle(fontSize: 18)),
),
_children,
],
),
),
),
);
}
}
遵循此文档:Cloud Messaging