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 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 tappedMessage = ValueNotifier(null); /// Call once after Firebase.initializeApp(), passing the instanceId. static Future 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 subscribeToInstance(String instanceId) async { await FirebaseMessaging.instance .subscribeToTopic('instance_$instanceId'); } static Future unsubscribeFromInstance(String instanceId) async { await FirebaseMessaging.instance .unsubscribeFromTopic('instance_$instanceId'); } static Future _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), ); } }