I'm trying to set up my notifications to work on android, as I am now preparing to launch my app on the Google Play Store. Here is the code so far:
// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/actions/index.dart'; // Imports other custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom action code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'dart:io';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter/foundation.dart';
Future<void> scheduleNotificationsForEvent(String eventId) async {
if (kIsWeb) return;
try {
final plugin = FlutterLocalNotificationsPlugin();
// 1) Initialize plugin on BOTH platforms, first thing.
const androidInit = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosInit = DarwinInitializationSettings();
const initSettings =
InitializationSettings(android: androidInit, iOS: iosInit);
await plugin.initialize(initSettings);
// 2) Android permission + channel (must exist before schedule).
final android = plugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
await android?.requestNotificationsPermission();
if (Platform.isAndroid) {
const ch = AndroidNotificationChannel(
'event_channel',
'Event Reminders',
description: 'Notifications for scheduled events',
importance: Importance.max,
playSound: true,
);
await android?.createNotificationChannel(ch);
}
// 3) Timezone setup (once, robust fallback).
tz.initializeTimeZones();
try {
final tzName = await FlutterTimezone.getLocalTimezone();
if (tz.timeZoneDatabase.locations.containsKey(tzName)) {
tz.setLocalLocation(tz.getLocation(tzName));
} else {
tz.setLocalLocation(tz.getLocation('UTC'));
}
} catch (e) {
tz.setLocalLocation(tz.getLocation('UTC'));
print('⚠️ Using UTC fallback for timezone: $e');
}
// 4) Fetch event.
final snap = await FirebaseFirestore.instance
.collection('events')
.doc(eventId)
.get();
if (!snap.exists || snap.data() == null) {
print('⚠️ Event not found or empty: $eventId');
return;
}
final data = snap.data()!;
final eventName = (data['event_name'] as String?)?.trim() ?? 'Event';
final ts = data['event_dateTime'] as Timestamp?;
if (ts == null) {
print('⚠️ event_dateTime is null for $eventId');
return;
}
// 5) Times.
final now = tz.TZDateTime.now(tz.local);
final eventTime = tz.TZDateTime.from(ts.toDate(), tz.local);
final oneHourBefore = eventTime.subtract(const Duration(hours: 1));
// 6) Stable IDs
// Android: 32-bit safe ids; iOS: keep original approach.
int _stableId(String seed) {
int h = 5381;
for (final code in seed.codeUnits) {
h = ((h << 5) + h) ^ code; // h*33 ^ code
}
return h & 0x7fffffff; // 0..2^31-1
}
late final int id1;
late final int id2;
if (Platform.isAndroid) {
// 32-bit safe (Android)
id1 = _stableId('$eventId|pre|${oneHourBefore.millisecondsSinceEpoch}');
id2 = _stableId('$eventId|at|${eventTime.millisecondsSinceEpoch}');
} else {
// Original logic for iOS (and others)
final base = eventId.codeUnits.fold<int>(0, (a, b) => a + b);
id1 = base ^ oneHourBefore.millisecondsSinceEpoch;
id2 = base ^ eventTime.millisecondsSinceEpoch;
}
// 7) Notification details.
const androidDetails = AndroidNotificationDetails(
'event_channel',
'Event Reminders',
channelDescription: 'Notifications for scheduled events',
importance: Importance.max,
priority: Priority.high,
playSound: true,
enableVibration: true,
);
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const details =
NotificationDetails(android: androidDetails, iOS: iosDetails);
// 8) Schedule (no matchDateTimeComponents for one-offs).
print(
'🕒 now=$now event=$eventTime oneHourBefore=$oneHourBefore tz=${tz.local}');
if (oneHourBefore.isAfter(now)) {
await plugin.zonedSchedule(
id1,
'Upcoming event in 1 hour',
eventName,
oneHourBefore,
details,
androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
print('⏰ Scheduled -1h @ $oneHourBefore (local) [id=$id1]');
} else {
print('⏭️ Skipped -1h (past)');
}
if (eventTime.isAfter(now)) {
await plugin.zonedSchedule(
id2,
eventName,
'',
eventTime,
details,
androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
print('✅ Scheduled event @ $eventTime (local) [id=$id2]');
} else {
print('⏭️ Skipped event-time (past)');
}
} catch (e) {
print('🔥 scheduleNotificationsForEvent error: $e');
}
}
in the device logs when i run on an android emulator, the logs show the the notification is scheduled, but when the time comes, nothing fires.