firebase_messaging 14.9.1 firebase_messaging: ^14.9.1 copied to clipboard
Flutter plugin for Firebase Cloud Messaging, a cross-platform messaging solution that lets you reliably deliver messages on Android and iOS.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:http/http.dart' as http;
import 'firebase_options.dart';
import 'message.dart';
import 'message_list.dart';
import 'permissions.dart';
import 'token_monitor.dart';
/// Working example of FirebaseMessaging.
/// Please use this in order to verify messages are working in foreground, background & terminated state.
/// Setup your app following this guide:
/// https://firebase.google.com/docs/cloud-messaging/flutter/client#platform-specific_setup_and_requirements):
///
/// Once you've completed platform specific requirements, follow these instructions:
/// 1. Install melos tool by running `flutter pub global activate melos`.
/// 2. Run `melos bootstrap` in FlutterFire project.
/// 3. In your terminal, root to ./packages/firebase_messaging/firebase_messaging/example directory.
/// 4. Run `flutterfire configure` in the example/ directory to setup your app with your Firebase project.
/// 5. Open `token_monitor.dart` and change `vapidKey` to yours.
/// 6. Run the app on an actual device for iOS, android is fine to run on an emulator.
/// 7. Use the following script to send a message to your device: scripts/send-message.js. To run this script,
/// you will need nodejs installed on your computer. Then the following:
/// a. Download a service account key (JSON file) from your Firebase console, rename it to "google-services.json" and add to the example/scripts directory.
/// b. Ensure your device/emulator is running, and run the FirebaseMessaging example app using `flutter run`.
/// c. Copy the token that is printed in the console and paste it here: https://github.com/firebase/flutterfire/blob/01b4d357e1/packages/firebase_messaging/firebase_messaging/example/lib/main.dart#L32
/// c. From your terminal, root to example/scripts directory & run `npm install`.
/// d. Run `npm run send-message` in the example/scripts directory and your app will receive messages in any state; foreground, background, terminated.
/// Note: Flutter API documentation for receiving messages: https://firebase.google.com/docs/cloud-messaging/flutter/receive
/// Note: If you find your messages have stopped arriving, it is extremely likely they are being throttled by the platform. iOS in particular
/// are aggressive with their throttling policy.
///
/// To verify that your messages are being received, you ought to see a notification appearon your device/emulator via the flutter_local_notifications plugin.
/// Define a top-level named handler which background/terminated messages will
/// call. Be sure to annotate the handler with `@pragma('vm:entry-point')` above the function declaration.
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await setupFlutterNotifications();
showFlutterNotification(message);
// 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.
print('Handling a background message ${message.messageId}');
}
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
bool isFlutterLocalNotificationsInitialized = false;
Future<void> setupFlutterNotifications() async {
if (isFlutterLocalNotificationsInitialized) {
return;
}
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'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,
);
isFlutterLocalNotificationsInitialized = true;
}
void showFlutterNotification(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,
channelDescription: channel.description,
// TODO add a proper drawable resource to android, for now using
// one that already exists in example app.
icon: 'launch_background',
),
),
);
}
}
/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
// Set the background messaging handler early on, as a named top-level function
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
if (!kIsWeb) {
await setupFlutterNotifications();
}
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;
String? initialMessage;
bool _resolved = false;
@override
void initState() {
super.initState();
FirebaseMessaging.instance.getInitialMessage().then(
(value) => setState(
() {
_resolved = true;
initialMessage = value?.data.toString();
},
),
);
FirebaseMessaging.onMessage.listen(showFlutterNotification);
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(
'Initial Message',
Column(
children: [
Text(_resolved ? 'Resolved' : 'Resolving'),
Text(initialMessage ?? 'None'),
],
),
),
MetaCard(
'FCM Token',
TokenMonitor((token) {
_token = token;
return token == null
? const CircularProgressIndicator()
: SelectableText(
token,
style: const TextStyle(fontSize: 12),
);
}),
),
ElevatedButton(
onPressed: () {
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage? message) {
if (message != null) {
Navigator.pushNamed(
context,
'/message',
arguments: MessageArguments(message, true),
);
}
});
},
child: const Text('getInitialMessage()'),
),
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,
],
),
),
),
);
}
}