Allow background sound notification

This commit is contained in:
2026-02-26 21:43:05 +08:00
parent 6ccf820438
commit 9cc99e612a
7 changed files with 265 additions and 142 deletions
+55 -8
View File
@@ -11,23 +11,50 @@ import 'package:firebase_messaging/firebase_messaging.dart';
import 'firebase_options.dart';
import 'providers/profile_provider.dart';
import 'models/profile.dart';
import 'app.dart';
import 'providers/notifications_provider.dart';
import 'utils/app_time.dart';
import 'utils/notification_permission.dart';
import 'services/notification_service.dart';
import 'services/notification_bridge.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
// Initialize the plugin
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
/// Handle messages received while the app is terminated or in background.
@pragma('vm:entry-point') // Required for background execution
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// Do minimal work in background isolate. Avoid initializing Flutter
// plugins here (e.g. flutter_local_notifications) as that can hang
// the background isolate and interfere with subsequent launches.
// If you need to persist data from a background message, perform a
// lightweight HTTP call or write to a background-capable DB.
return;
// 1. Initialize the plugin inside the background isolate
final FlutterLocalNotificationsPlugin localNotifPlugin =
FlutterLocalNotificationsPlugin();
// 2. Extract title and body from the DATA payload (not message.notification)
final String title = message.data['title'] ?? 'New Notification';
final String body = message.data['body'] ?? 'You have a new update in TasQ.';
// Create a unique ID
final int id = DateTime.now().millisecondsSinceEpoch ~/ 1000;
// 3. Define the exact same channel specifics
const androidDetails = AndroidNotificationDetails(
'tasq_custom_sound_channel_2',
'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
playSound: true,
sound: RawResourceAndroidNotificationSound('tasq_notification'),
);
// 4. Show the notification manually
await localNotifPlugin.show(
id: id,
title: title,
body: body,
notificationDetails: const NotificationDetails(android: androidDetails),
payload: message.data['type'], // Or whatever payload you need for routing
);
}
Future<void> main() async {
@@ -97,6 +124,7 @@ Future<void> main() async {
// request FCM permission (iOS/Android13+) and handle foreground messages
await FirebaseMessaging.instance.requestPermission();
}
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
final notification = message.notification;
if (notification != null) {
@@ -122,6 +150,25 @@ Future<void> main() async {
},
);
// 1. Define the High Importance Channel (This MUST match your manifest exactly)
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'tasq_custom_sound_channel', // id
'High Importance Notifications', // title visible to user in phone settings
description: 'This channel is used for important TasQ notifications.',
importance:
Importance.max, // THIS is what forces the sound and heads-up banner
playSound: true,
// 👇 Tell Android to use your specific file in the raw folder
sound: RawResourceAndroidNotificationSound('tasq_notification'),
);
// 2. Create the channel on the device
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.createNotificationChannel(channel);
// global navigator key used for snackbars/navigation from notification
final navigatorKey = GlobalKey<NavigatorState>();
+45 -4
View File
@@ -7,6 +7,10 @@ class NotificationService {
static final FlutterLocalNotificationsPlugin _plugin =
FlutterLocalNotificationsPlugin();
static const String _channelId = 'tasq_default_channel';
static const String _channelName = 'General';
static const String _highChannelId = 'tasq_custom_sound_channel_2';
static const String _highChannelName = 'High Priority';
/// Call during app startup, after any necessary permissions have been
/// granted. The callback receives a [NotificationResponse] when the user
@@ -19,6 +23,38 @@ class NotificationService {
);
const iosSettings = DarwinInitializationSettings();
// Ensure the Android notification channel exists with sound and vibration
final androidImpl = _plugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>();
if (androidImpl != null) {
const channel = AndroidNotificationChannel(
_channelId,
_channelName,
description: 'General notifications for TasQ',
importance: Importance.defaultImportance,
playSound: true,
enableVibration: true,
);
const highChannel = AndroidNotificationChannel(
_highChannelId,
_highChannelName,
description: 'High priority notifications (sound + vibration)',
importance: Importance.max,
playSound: true,
enableVibration: true,
// 👇 Tell Android to use your specific file in the raw folder
sound: RawResourceAndroidNotificationSound('tasq_notification'),
);
try {
await androidImpl.createNotificationChannel(channel);
} catch (_) {}
try {
await androidImpl.createNotificationChannel(highChannel);
} catch (_) {}
}
await _plugin.initialize(
settings: const InitializationSettings(
android: androidSettings,
@@ -36,12 +72,17 @@ class NotificationService {
String? payload,
}) async {
const androidDetails = AndroidNotificationDetails(
'default_channel',
'General',
importance: Importance.high,
_highChannelId,
_highChannelName,
importance: Importance.max,
priority: Priority.high,
playSound: true,
enableVibration: true,
sound: RawResourceAndroidNotificationSound('tasq_notification'),
);
const iosDetails = DarwinNotificationDetails(
sound: 'tasq_notification.wav',
);
const iosDetails = DarwinNotificationDetails();
await _plugin.show(
id: id,
title: title,