import 'dart:developer' as dev; import 'dart:math' as math; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart'; class NotificationServices { FirebaseMessaging messaging = FirebaseMessaging.instance; final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); Future initLocalNotifications(PersistentTabController controller) async { const AndroidInitializationSettings androidInitializationSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); const DarwinInitializationSettings iosInitializationSettings = DarwinInitializationSettings(); const InitializationSettings initializationSettings = InitializationSettings( android: androidInitializationSettings, iOS: iosInitializationSettings, ); await _flutterLocalNotificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse response) { dev.log("Người dùng click thông báo ở foreground với payload: ${response.payload}"); handleMessage(response.payload,controller); }, ); dev.log("Local notifications initialized"); } void firebaseInit(BuildContext context) { FirebaseMessaging.onMessage.listen((message) { dev.log("Foreground message payload: ${message.toMap()}"); if (WidgetsBinding.instance != null) { showNotification(message); } else { dev.log("App is in background, skipping foreground notification"); } }); } Future getDeviceToken() async { String? token = await messaging.getToken(); return token!; } void isTokenRefresh() { messaging.onTokenRefresh.listen((newToken) { dev.log("Refresh Firebase Messaging Token: $newToken"); }); } Future showNotification(RemoteMessage message) async { String? title = message.data['title']; String? body = message.data['body']; String type = message.data['type'] ?? "normal"; if (title == null || body == null) { dev.log("Skipping notification due to missing title or body"); return; } AndroidNotificationChannel androidNotificationChannel = AndroidNotificationChannel( math.Random.secure().nextInt(1000000).toString(), 'High Importance Notification', importance: Importance.max, ); final androidPlugin = _flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation(); await androidPlugin?.deleteNotificationChannel(androidNotificationChannel.id); AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails( androidNotificationChannel.id, androidNotificationChannel.name, channelDescription: "Channel description", sound: getSound(type), importance: androidNotificationChannel.importance, priority: Priority.high, ticker: 'ticker', actions: type == "smoke_warning" ? [ const AndroidNotificationAction( "id1", "Hogg xóa được", // true thì khi nhấn vào button sẽ mở giao diện ra showsUserInterface: true, cancelNotification: false, ) ] : null ); const DarwinNotificationDetails darwinNotificationDetails = DarwinNotificationDetails( presentAlert: true, presentBadge: true, presentBanner: true, presentSound: true, ); NotificationDetails notificationDetails = NotificationDetails( android: androidNotificationDetails, iOS: darwinNotificationDetails, ); // Truyền payload vào thông báo String payload = message.data['type'] ?? "default"; await _flutterLocalNotificationsPlugin.show( math.Random.secure().nextInt(1000000), title, body, notificationDetails, payload: payload, ); dev.log("Displayed notification with title: $title, body: $body, type: $type"); } AndroidNotificationSound getSound(String type) { if (type == "welcome") { return const RawResourceAndroidNotificationSound("welcome"); } else if (type == "success") { return const RawResourceAndroidNotificationSound("success_alert"); } else if (type == "smoke_warning") { return const RawResourceAndroidNotificationSound("warning_alarm"); } else if (type == "battery_warning") { return const RawResourceAndroidNotificationSound("new_alarm"); } else { return const RawResourceAndroidNotificationSound("normal"); } } void handleMessage(String? payload, PersistentTabController controller) { dev.log("Handling notification tap with payload: $payload"); controller.jumpToTab(1); } Future setupInteractMessage(PersistentTabController controller) async { // Khi app terminated RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { WidgetsBinding.instance.addPostFrameCallback((_) { try { handleMessage(initialMessage.data['type'],controller); } catch (e, stack) { dev.log("Error handling initial message: $e\n$stack"); } }); } // Khi app ở background FirebaseMessaging.onMessageOpenedApp.listen((message) { try { handleMessage(message.data['type'],controller); } catch (e, stack) { dev.log("Error in onMessageOpenedApp: $e\n$stack"); } }); } }