Geplante Flutter-Zone-Benachrichtigungen funktionieren im Vordergrund/Hintergrund, sind jedoch im beendeten Zustand auf Android

Forum für diejenigen, die für Android programmieren
Anonymous
 Geplante Flutter-Zone-Benachrichtigungen funktionieren im Vordergrund/Hintergrund, sind jedoch im beendeten Zustand auf

Post by Anonymous »

Ich arbeite an einer Flutter-Erinnerungs-App mit geplanten lokalen Benachrichtigungen.
Verhalten, das ich sehe
Auf dem Emulator
  • Funktioniert im Vordergrund
  • Funktioniert im Hintergrund
  • Funktioniert im beendeten Zustand

    Benachrichtigungen werden in allen Fällen pünktlich ausgelöst.
Auf echtem Android-Gerät
  • Funktioniert im Vordergrund
  • Funktioniert im Hintergrund
  • Unzuverlässig im beendeten Zustand
Auf realem Gerät beobachtete Muster
  • Benachrichtigungen funktionieren nur, wenn sie weit voneinander entfernt sind
    • Beispiel:

      8:15 Uhr und 8:30 Uhr → beide feuern
    • 8:15 Uhr und 8:16 Uhr → einer oder beide feuern nicht
  • Benachrichtigungen, die sehr nah beieinander geplant sind, werden oft übersprungen.
  • Verpasste Benachrichtigungen werden angezeigt, wenn das Gerät an USB angeschlossen ist
    • Wenn einige Benachrichtigungen nicht angezeigt werden,
    • Wenn das Telefon an USB angeschlossen ist und die App ausgeführt wird,
    • Alle zuvor verpassten Benachrichtigungen werden auf einmal angezeigt.
Was ich verstehen möchte
  • Warum funktioniert das zuverlässig auf dem Emulator, aber nicht auf einem echten Gerät?
  • Warum schlagen nahe beieinander geplante Benachrichtigungen auf echten Geräten fehl?
  • Warum werden verpasste Benachrichtigungen plötzlich ausgelöst, wenn das Gerät angeschlossen wird? USB?
  • Hängt dieses Verhalten mit dem Android Doze-Modus, der Alarm-Batching-Funktion oder der Batterieoptimierung zusammen?
Ich suche:
  • Erklärung der Grundursache
  • Was ist die Lösung für diese Situation?
Android-Manifestdatei:

Code: Select all












































Benachrichtigungsdienstdatei (Initialisierung und Zeitplan)

Code: Select all


import 'package:digital_remainder/core/core.dart';
// import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;

class NotificationService {
static final FlutterLocalNotificationsPlugin notificationsPlugin =
FlutterLocalNotificationsPlugin();

//notification service intialization
static Future initialize() async {
const AndroidInitializationSettings androidSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');

const DarwinInitializationSettings iosSettings =
DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);

const InitializationSettings initializationSettings =
InitializationSettings(android: androidSettings, iOS: iosSettings);

await notificationsPlugin.initialize(initializationSettings);

//initialize timezone
initializeTimeZones();

//check notification, alarm permission, and bcg optimization
await NotificationPermission.askRequiredPermissions();
}

//android and ios notification details
static Future reminderNotificationDetails(
ReminderPriorityOptions priority,
) async {
final androidDetails = AndroidNotificationDetails(
'reminder_channel',
'Reminder Notifications',
channelDescription: 'Notifications for Digital Reminders',
importance: priority == ReminderPriorityOptions.high
? Importance.max
: priority == ReminderPriorityOptions.medium
? Importance.defaultImportance
: Importance.low,
priority: priority == ReminderPriorityOptions.high
? Priority.high
: priority == ReminderPriorityOptions.medium
? Priority.defaultPriority
: Priority.low,
playSound: true,
enableVibration: true,
// icon: '@drawable/ic_app_notification',
visibility: NotificationVisibility.public,
// sound: RawResourceAndroidNotificationSound("notification_sound"),
// color: AppColors.primary,
);
const iosDetails = DarwinNotificationDetails(
// sound: "notification_sound.wav",
presentAlert: true,
presentBadge: true,
presentSound: true,
);

final notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
return notificationDetails;
}

//schedule reminder notifications
static Future  schedule({
required String id,
required String title,
required String body,
required DateTime dateTime,
required ReminderRepeatOptions repeat,
required ReminderPriorityOptions priority,
}) async {
try {
final details = await reminderNotificationDetails(priority);

final int notifId = id.hashCode;

tz.TZDateTime scheduledDate = tz.TZDateTime.from(dateTime, tz.local);
if (scheduledDate.isBefore(tz.TZDateTime.now(tz.local))) {
return;
}

// check if exact alarms are allowed on Android
// final androidPlugin = notificationsPlugin
//     .resolvePlatformSpecificImplementation<
//       AndroidFlutterLocalNotificationsPlugin
//     >();

// final exactAllowed =
//     await androidPlugin?.requestExactAlarmsPermission() ?? false;

// AndroidScheduleMode androidScheduleMode = exactAllowed
//     ? AndroidScheduleMode.alarmClock
//     : AndroidScheduleMode.exactAllowWhileIdle;
AndroidScheduleMode androidScheduleMode = AndroidScheduleMode.alarmClock;
// AndroidScheduleMode androidScheduleMode =
//     AndroidScheduleMode.exactAllowWhileIdle;

DateTimeComponents? matchDateTimeComponents;

switch (repeat) {
case ReminderRepeatOptions.daily:
scheduledDate = tz.TZDateTime(
tz.local,
dateTime.year,
dateTime.month,
dateTime.day,
dateTime.hour,
dateTime.minute,
);
matchDateTimeComponents = DateTimeComponents.time;
break;

case ReminderRepeatOptions.weekly:
scheduledDate = tz.TZDateTime(
tz.local,
dateTime.year,
dateTime.month,
dateTime.day,
dateTime.hour,
dateTime.minute,
);
matchDateTimeComponents = DateTimeComponents.dayOfWeekAndTime;
break;

case ReminderRepeatOptions.monthly:
scheduledDate = tz.TZDateTime(
tz.local,
dateTime.year,
dateTime.month,
dateTime.day,
dateTime.hour,
dateTime.minute,
);
matchDateTimeComponents = DateTimeComponents.dayOfMonthAndTime;
break;

default:
// matchDateTimeComponents = DateTimeComponents.dateAndTime;
matchDateTimeComponents = null;
break;
}

await notificationsPlugin.zonedSchedule(
notifId,
title,
body,
scheduledDate,
details,
androidScheduleMode: androidScheduleMode,
matchDateTimeComponents: matchDateTimeComponents,
payload: scheduledDate.toString(),
);
// debugPrint('Scheduling for: ${scheduledDate.toString()}');
// debugPrint('Now: ${tz.TZDateTime.now(tz.local)}');

// debugPrint('getting the list of set schedules:.....');
// final list = await notificationsPlugin.pendingNotificationRequests();
// debugPrint('calculating length --- :.....');
// debugPrint(list.length.toString());
// debugPrint('schedule item  --- :.....');
// for (var item in list) {
//   debugPrint(' --- :.....');
//   debugPrint(item.title.toString());
//   debugPrint(item.body.toString());
//   debugPrint(item.payload.toString());
//   debugPrint(' --- :.....');
// }
} catch (e) {
// print("errror: $e");
}
}

//cancel specific scheduled notification
static Future cancel(String id) async {
final int notifId = id.hashCode;

await notificationsPlugin.cancel(notifId);
}

//cancel all scheduled notifications
static Future cancelAll() async {
await notificationsPlugin.cancelAll();
}

//for showing instance notification : testing
static Future  showInstantNotification(
int id,
String? title,
String? body,
) async {
NotificationDetails notificationDetails = await reminderNotificationDetails(
ReminderPriorityOptions.high,
);

await notificationsPlugin.show(id, title, body, notificationDetails);
}
}
Benachrichtigungsberechtigungsdatei:

Code: Select all


import 'dart:io';
import 'package:digital_remainder/core/core.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:permission_handler/permission_handler.dart';

class NotificationPermission {
static final FlutterLocalNotificationsPlugin notificationsPlugin =
NotificationService.notificationsPlugin;
static Future askRequiredPermissions() async {
await _askNotificationPermission();
await _askExactAlarmPermission();
await _askBatteryOptimizationPermission();
}

static Future _askNotificationPermission() async {
var status = await Permission.notification.status;
if (!status.isGranted) {
var request = await Permission.notification.request();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.requestNotificationsPermission();
if (request.isGranted) {
CustomSnackbar.showToastMessage(
type: ToastType.success,
message: 'Notification Permission Granted.',
);
} else {
CustomSnackbar.showToastMessage(
type: ToastType.info,
message: 'Notification Permission Denied.',
);
}
}
}

static Future _askExactAlarmPermission() async {
if (!Platform.isAndroid) return;

final androidPlugin = notificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>();
final canSchedule = await androidPlugin?.canScheduleExactNotifications();

if (canSchedule == false) {
CustomSnackbar.showToastMessage(
type: ToastType.info,
message: 'Please allow Exact Alarm for reminders to work properly.',
);
await androidPlugin?.requestExactAlarmsPermission();
}
}

static Future _askBatteryOptimizationPermission() async {
final status = await Permission.ignoreBatteryOptimizations.status;
if (!status.isGranted) {
var request = await Permission.ignoreBatteryOptimizations.request();
if (request.isGranted) {
CustomSnackbar.showToastMessage(
type: ToastType.success,
message: 'Battery Optimization Ignored.',
);
} else {
CustomSnackbar.showToastMessage(
type: ToastType.info,
message: 'Permission Denied.',
);
}
}
}
}
Zeitzoneninitialisierung:

Code: Select all


import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

void initializeTimeZones() async {
final TimezoneInfo currentTimeZone = await FlutterTimezone.getLocalTimezone();
final localLocation = currentTimeZone.identifier;
tz.initializeTimeZones();
tz.setLocalLocation(tz.getLocation(localLocation));
}
Erinnerungsplanungshelfer:

Code: Select all


import 'package:digital_remainder/core/core.dart';
import 'package:digital_remainder/modules/reminder/reminder.dart';

class ReminderScheduler {
static Future add(ReminderEntity entity) async {
final scheduledDateTime = DateTime(
entity.date.year,
entity.date.month,
entity.date.day,
entity.time.hour,
entity.time.minute,
);
await NotificationService.schedule(
id: entity.id!,
title: entity.title,
body: entity.description ?? 'Time for this reminder',
dateTime: scheduledDateTime,
repeat: entity.repeat,
priority: entity.priority,
);
}

static Future  update(ReminderEntity entity) async {
await NotificationService.cancel(entity.id!);
if (entity.alertNotification) {
await add(entity);
}
}

static Future delete(String id) async {
await NotificationService.cancel(id);
}
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post