update sound when receive from FCM in foreground, background and terminate

This commit is contained in:
anhtunz
2025-03-07 16:44:55 +07:00
parent 314e32eaa9
commit a6fa3b1572
12 changed files with 185 additions and 67 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/new_alarm.wav Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -192,6 +192,8 @@ class _LoginScreenState extends State<LoginScreen> {
LocaleManager.instance
.setString(PreferencesKeys.ROLE, loginModel.role!);
context.goNamed(AppRoutes.HOME.name);
// context.goNamed("notification");
} else {
showErrorTopSnackBarCustom(
context, appLocalization(context).login_incorrect_usernameOrPass);
@@ -214,6 +216,7 @@ class _LoginScreenState extends State<LoginScreen> {
int timeNow = DateTime.now().millisecondsSinceEpoch ~/ 1000;
if (token != "" && (exp - timeNow) > 7200) {
context.goNamed(AppRoutes.HOME.name);
// context.goNamed("notification");
}
}

View File

@@ -1,6 +1,10 @@
import 'dart:developer';
// ignore_for_file: avoid_print
import 'dart:math' as math;
import 'dart:developer' as dev;
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'notification_bloc.dart';
import '../../product/base/bloc/base_bloc.dart';
import '../../product/services/notification_services.dart';
@@ -14,11 +18,12 @@ class NotificationScreen extends StatefulWidget {
class _NotificationScreenState extends State<NotificationScreen> {
late NotificationBloc notificationBloc;
NotificationServices notificationServices = NotificationServices();
final notificationPlugin = FlutterLocalNotificationsPlugin();
@override
void initState() {
super.initState();
initNotification();
notificationBloc = BlocProvider.of<NotificationBloc>(context);
}
@@ -30,11 +35,43 @@ class _NotificationScreenState extends State<NotificationScreen> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextButton(
onPressed: () {
log("Token: ${notificationServices.getDeviceToken()}");
onPressed: () async {
showNewAlarmSoundNotification("warning_alarm");
dev.log("Da vao day");
},
child: Text("Get Notification Token"))
child: const Text("Show new Alarm Notification"),
),
],
));
}
Future<void> initNotification() async{
const isSettingAndroid = AndroidInitializationSettings('@mipmap/ic_launcher');
const initSetting = InitializationSettings(android: isSettingAndroid);
await notificationPlugin.initialize(initSetting);
}
Future<void> showNewAlarmSoundNotification(String sound) async {
AndroidNotificationChannel androidNotificationChannel =
AndroidNotificationChannel(
math.Random.secure().nextInt(1000000).toString(),
'high Important Notification',
importance: Importance.max);
AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
"androidNotificationChannel.id.toString()",
androidNotificationChannel.name.toString(),
sound: RawResourceAndroidNotificationSound(sound),
channelDescription: "Channel description",
importance: androidNotificationChannel.importance,
priority: Priority.high,
ticker: 'ticker',
);
final NotificationDetails notificationDetails =
NotificationDetails(android: androidNotificationDetails);
await FlutterLocalNotificationsPlugin().show(0, 'New Notification',
'Sound Notification Example', notificationDetails);
}
}

View File

@@ -1,6 +1,10 @@
import 'dart:developer';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'product/services/notification_services.dart';
import 'product/services/theme_services.dart';
import 'product/services/language_services.dart';
import 'bloc/main_bloc.dart';
@@ -10,6 +14,10 @@ import 'product/constant/navigation/navigation_router.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging
.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
NotificationServices().setupInteractMessage();
runApp(
BlocProvider(
child: const MyApp(),
@@ -17,6 +25,16 @@ void main() async {
),
);
}
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
log("Full background message payload: ${message.toMap()}");
await Firebase.initializeApp();
final notificationServices = NotificationServices();
await notificationServices.initLocalNotifications();
await notificationServices.showNotification(message);
log("Background message handled: ${message.data['title']}");
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@@ -39,6 +57,7 @@ class _MyAppState extends State<MyApp> {
late MainBloc mainBloc;
LanguageServices languageServices = LanguageServices();
ThemeServices themeServices = ThemeServices();
final NotificationServices notificationServices = NotificationServices();
setLocale(Locale locale) {
_locale = locale;
@@ -49,11 +68,16 @@ class _MyAppState extends State<MyApp> {
_themeData = theme;
mainBloc.sinkTheme.add(_themeData);
}
@override
void initState() {
super.initState();
mainBloc = BlocProvider.of(context);
notificationServices.initLocalNotifications();
notificationServices.firebaseInit(context);
// notificationServices.setupInteractMessage();
notificationServices.getDeviceToken().then((token){
print("Firebase Token: $token");
});
}
@override

View File

@@ -1,6 +1,6 @@
import 'package:go_router/go_router.dart';
import 'package:sfm_app/feature/sound_notification_test/notification_bloc.dart';
import 'package:sfm_app/feature/sound_notification_test/notification_screen.dart';
import '../../../feature/sound_notification_test/notification_bloc.dart';
import '../../../feature/sound_notification_test/notification_screen.dart';
import '../../../bloc/device_detail_bloc.dart';
import '../../../feature/devices/device_detail/device_detail_screen.dart';
import '../../../bloc/device_notification_settings_bloc.dart';

View File

@@ -1,5 +1,3 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:developer' as dev;
import 'dart:math' as math;
import 'package:firebase_messaging/firebase_messaging.dart';
@@ -8,15 +6,34 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationServices {
FirebaseMessaging messaging = FirebaseMessaging.instance;
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
void firebaseInit(BuildContext context) async {
Future<void> initLocalNotifications() 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);
},
);
dev.log("Local notifications initialized");
}
void firebaseInit(BuildContext context) {
FirebaseMessaging.onMessage.listen((message) {
initNotifications(context, message);
dev.log("Foreground message payload: ${message.toMap()}");
if (WidgetsBinding.instance != null) {
showNotification(message);
dev.log(
"Title: ${message.notification!.title}, Body: ${message.notification!.body} from ${message.sentTime}");
} else {
dev.log("App is in background, skipping foreground notification");
}
});
}
@@ -25,80 +42,116 @@ class NotificationServices {
return token!;
}
void isTokenRefresh() async {
void isTokenRefresh() {
messaging.onTokenRefresh.listen((newToken) {
dev.log("Refresh Firebase Messaging Token: $newToken");
});
}
void initNotifications(BuildContext context, RemoteMessage message) async {
var androidInitializationSettings =
const AndroidInitializationSettings('app_icon');
var iosInitializationSettings = const DarwinInitializationSettings();
var initializationSettings = InitializationSettings(
android: androidInitializationSettings, iOS: iosInitializationSettings);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings,
onDidReceiveNotificationResponse: (payload) {
handleMessage(context, message);
});
Future<void> 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;
}
Future<void> showNotification(RemoteMessage message) async {
AndroidNotificationChannel androidNotificationChannel =
AndroidNotificationChannel(
AndroidNotificationChannel androidNotificationChannel = AndroidNotificationChannel(
math.Random.secure().nextInt(1000000).toString(),
'high Important Notification',
importance: Importance.max);
AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
androidNotificationChannel.id.toString(),
androidNotificationChannel.name.toString(),
sound: RawResourceAndroidNotificationSound(message.data['sound']),
'High Importance Notification',
importance: Importance.max,
);
final androidPlugin = _flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
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 == "warn1"
? [
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(
const DarwinNotificationDetails darwinNotificationDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentBanner: true,
presentSound: true,
);
NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails, iOS: darwinNotificationDetails);
Future.delayed(Duration.zero, () {
_flutterLocalNotificationsPlugin.show(0, message.notification!.title!,
message.notification!.body, 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");
}
void handleMessage(BuildContext context, RemoteMessage message) async {
if (message.data['type'] == "msj") {
// Navigator.push(context,
// MaterialPageRoute(builder: (context) => const MessageScreen()));
} else if (message.data['type'] == "warn") {
// Navigator.push(
// context, MaterialPageRoute(builder: (context) => const MapScreen()));
AndroidNotificationSound getSound(String type) {
if (type == "welcome") {
return const RawResourceAndroidNotificationSound("welcome");
} else if (type == "success") {
return const RawResourceAndroidNotificationSound("success_alert");
} else if (type == "warn1") {
return const RawResourceAndroidNotificationSound("warning_alarm");
} else if (type == "warn2") {
return const RawResourceAndroidNotificationSound("new_alarm");
} else {
dev.log("Not found data");
return const RawResourceAndroidNotificationSound("normal");
}
}
Future<void> setupInteractMessage(BuildContext context) async {
// When app terminate
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
void handleMessage(String? payload) {
dev.log("Handling notification tap with payload: $payload");
// Thêm logic xử lý khi nhấn thông báo ở đây
// Ví dụ: Điều hướng màn hình hoặc xử lý dữ liệu
}
Future<void> setupInteractMessage() async {
// Khi app terminated
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
// showNotification(initialMessage)
handleMessage(context, initialMessage);
WidgetsBinding.instance.addPostFrameCallback((_) {
try {
handleMessage(initialMessage.data['type']);
} catch (e, stack) {
dev.log("Error handling initial message: $e\n$stack");
}
// When app is inBackGround
});
}
// Khi app ở background
FirebaseMessaging.onMessageOpenedApp.listen((message) {
handleMessage(context, message);
try {
handleMessage(message.data['type']);
} catch (e, stack) {
dev.log("Error in onMessageOpenedApp: $e\n$stack");
}
});
}
}

View File

@@ -92,6 +92,7 @@ flutter:
- assets/images/
- assets/icons/
- assets/map_themes/
- assets/sounds/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware