import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter/foundation.dart'; class NotificationService { static final NotificationService _instance = NotificationService._internal(); factory NotificationService() => _instance; NotificationService._internal(); final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin(); bool _initialized = false; Future initialize() async { if (_initialized) return; try { // Android initialization const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); // iOS initialization const DarwinInitializationSettings iosSettings = DarwinInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); // Linux/Windows initialization const LinuxInitializationSettings linuxSettings = LinuxInitializationSettings( defaultActionName: 'Open rmtPocketWatcher', ); const InitializationSettings settings = InitializationSettings( android: androidSettings, iOS: iosSettings, linux: linuxSettings, ); await _notifications.initialize( settings, onDidReceiveNotificationResponse: _onNotificationTapped, ); _initialized = true; if (kDebugMode) { print('NotificationService initialized successfully'); } } catch (e) { if (kDebugMode) { print('Failed to initialize notifications: $e'); } // Don't throw - allow app to continue without notifications } } void _onNotificationTapped(NotificationResponse response) { // Handle notification tap if needed if (kDebugMode) { print('Notification tapped: ${response.payload}'); } } Future showPriceAlert({ required String title, required String body, required double auecAmount, required double price, required String seller, }) async { if (!_initialized) await initialize(); if (!_initialized) return; // Skip if initialization failed try { // Android notification details const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'price_alerts', 'Price Alerts', channelDescription: 'Notifications for AUEC price alerts from rmtPocketWatcher', importance: Importance.high, priority: Priority.high, sound: RawResourceAndroidNotificationSound('notifcation'), icon: '@mipmap/ic_launcher', largeIcon: DrawableResourceAndroidBitmap('@mipmap/ic_launcher'), enableVibration: true, enableLights: true, showWhen: true, ); // iOS notification details const DarwinNotificationDetails iosDetails = DarwinNotificationDetails( sound: 'notifcation.mp3', // iOS looks in main bundle, not assets presentAlert: true, presentBadge: true, presentSound: true, badgeNumber: 1, subtitle: 'Lambda Banking Conglomerate', threadIdentifier: 'price_alerts', ); // Linux/Windows notification details final LinuxNotificationDetails linuxDetails = LinuxNotificationDetails( icon: AssetsLinuxIcon('assets/logo.png'), sound: AssetsLinuxSound('assets/notifcation.mp3'), category: LinuxNotificationCategory.imReceived, urgency: LinuxNotificationUrgency.critical, ); final NotificationDetails details = NotificationDetails( android: androidDetails, iOS: iosDetails, linux: linuxDetails, ); final int notificationId = DateTime.now().millisecondsSinceEpoch.remainder(100000); await _notifications.show( notificationId, title, body, details, payload: 'price_alert:$seller:$auecAmount:$price', ); if (kDebugMode) { print('Price alert notification sent: $title - $body'); } } catch (e) { if (kDebugMode) { print('Failed to show notification: $e'); } } } Future requestPermissions() async { if (!_initialized) await initialize(); // Request permissions for iOS await _notifications .resolvePlatformSpecificImplementation() ?.requestPermissions( alert: true, badge: true, sound: true, ); // Request permissions for Android 13+ await _notifications .resolvePlatformSpecificImplementation() ?.requestNotificationsPermission(); } }