94 lines
3.2 KiB
Dart
94 lines
3.2 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
|
|
/// Handles background FCM messages (must be top-level function)
|
|
@pragma('vm:entry-point')
|
|
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
// Firebase is already initialized in main — nothing to do here,
|
|
// the OS will display the notification automatically.
|
|
}
|
|
|
|
class PushNotificationService {
|
|
static final FlutterLocalNotificationsPlugin _localNotifications =
|
|
FlutterLocalNotificationsPlugin();
|
|
|
|
/// Notifier set when the user taps a notification (background or terminated).
|
|
static final ValueNotifier<RemoteMessage?> tappedMessage = ValueNotifier(null);
|
|
|
|
/// Call once after Firebase.initializeApp(), passing the instanceId.
|
|
static Future<void> initialize(String instanceId) async {
|
|
// Register background handler
|
|
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
|
|
|
|
// Request permission (iOS / Android 13+)
|
|
await FirebaseMessaging.instance.requestPermission(
|
|
alert: true,
|
|
badge: true,
|
|
sound: true,
|
|
);
|
|
|
|
// Configure local notifications for foreground display
|
|
const androidSettings =
|
|
AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
const iosSettings = DarwinInitializationSettings();
|
|
await _localNotifications.initialize(
|
|
const InitializationSettings(android: androidSettings, iOS: iosSettings),
|
|
);
|
|
|
|
// Subscribe to the instance topic
|
|
await subscribeToInstance(instanceId);
|
|
|
|
// Show local notification when app is in foreground
|
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
_showLocalNotification(message);
|
|
});
|
|
|
|
// App opened from background by tapping a notification
|
|
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
tappedMessage.value = message;
|
|
});
|
|
|
|
// App launched from terminated state by tapping a notification
|
|
final initial = await FirebaseMessaging.instance.getInitialMessage();
|
|
if (initial != null) {
|
|
Future.delayed(const Duration(milliseconds: 500), () {
|
|
tappedMessage.value = initial;
|
|
});
|
|
}
|
|
}
|
|
|
|
static Future<void> subscribeToInstance(String instanceId) async {
|
|
await FirebaseMessaging.instance
|
|
.subscribeToTopic('instance_$instanceId');
|
|
}
|
|
|
|
static Future<void> unsubscribeFromInstance(String instanceId) async {
|
|
await FirebaseMessaging.instance
|
|
.unsubscribeFromTopic('instance_$instanceId');
|
|
}
|
|
|
|
static Future<void> _showLocalNotification(RemoteMessage message) async {
|
|
final notification = message.notification;
|
|
if (notification == null) return;
|
|
|
|
const androidDetails = AndroidNotificationDetails(
|
|
'push_notifications',
|
|
'Push Notifications',
|
|
channelDescription: 'Notifications from the museum',
|
|
importance: Importance.high,
|
|
priority: Priority.high,
|
|
);
|
|
const iosDetails = DarwinNotificationDetails();
|
|
|
|
await _localNotifications.show(
|
|
notification.hashCode,
|
|
notification.title,
|
|
notification.body,
|
|
const NotificationDetails(android: androidDetails, iOS: iosDetails),
|
|
);
|
|
}
|
|
}
|