import 'dart:async'; import 'dart:developer'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:hub/firebase_options.dart'; class NotificationService { // Singleton instance static final NotificationService _instance = NotificationService._internal(); // Factory constructor factory NotificationService() => _instance; // Private constructor NotificationService._internal(); static final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); static bool _initialized = false; Future initialize() async { if (_initialized) return; _initialized = true; try { await _requestPermissions(); await _initializeLocalNotifications(); FirebaseMessaging.onBackgroundMessage( _firebaseMessagingBackgroundHandler); FirebaseMessaging.onMessage.listen((RemoteMessage message) { _printNotification(message, 'onMessage'); }); FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { _printNotification(message, 'onMessageOpenedApp'); }); } catch (e) { log('Error initializing notification manager: $e'); } } Future _printNotification(RemoteMessage message, String type) async { log('Notification $type: ${message.messageId}'); // Handle the notification display logic here } Future _initializeLocalNotifications() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); final DarwinInitializationSettings initializationSettingsIOS = DarwinInitializationSettings( onDidReceiveLocalNotification: (int id, String? title, String? body, String? payload) async { // Handle the notification received in foreground }, ); final InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, ); await _flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (payload) async => log('Notification tapped: $payload'), onDidReceiveBackgroundNotificationResponse: (payload) async => log('Notification tapped in background: $payload')); } Future _requestPermissions() async { NotificationSettings settings = await _firebaseMessaging.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true, ); log('User granted permission: ${settings.authorizationStatus}'); } Future subscribeToTopic(String topic) async { try { await _firebaseMessaging.subscribeToTopic(topic); log('Subscribed to topic: $topic'); } catch (e) { log('Error subscribing to topic $topic: $e'); } } Future unsubscribeFromTopic(String topic) async { try { await _firebaseMessaging.unsubscribeFromTopic(topic); log('Unsubscribed from topic: $topic'); } catch (e) { log('Error unsubscribing from topic $topic: $e'); } } } @pragma('vm:entry-point') Future _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. log('Handling a background message ${message.messageId}'); } /// Create a [AndroidNotificationChannel] for heads up notifications late AndroidNotificationChannel channel; bool isFlutterLocalNotificationsInitialized = false; Future 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;