gdelataillade / alarm

A Flutter plugin to easily manage alarms on iOS and Android
https://pub.dev/packages/alarm
MIT License
132 stars 86 forks source link

Need some help with ringstream listener and firebase.onBackgroundMessage #269

Open IkerCasillass opened 3 weeks ago

IkerCasillass commented 3 weeks ago

Is your feature request related to a problem? Please describe.

This issue isn't directly related to a problem with the package itself, but I could use some guidance on an implementation issue I'm encountering. I'm developing an app that needs to trigger an alarm when a push notification arrives. Everything works as expected when the app is in the foreground. However, when Firebase.onBackgroundMessage is called, the alarm triggers, but the ringstream listener doesn't seem to activate. This prevents the app from updating to display the ringing screen.

So, how can i make sure that the listener is set when the alarm triggers in the background?

Describe the solution you'd like I’d like the ringstream to be available when onBackgroundMessage() is called, so that the listener can respond appropriately and display the alarm screen.

Describe alternatives you've considered I think maybe it is because of how the ringstream listener is set in the initState() of the home screen, that may not be availible in the background. (Im using the example app).

Additional context As mentioned, I'm working with the example app, with Firebase Messaging functionality added.

Here is my code:

main.dart

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);

  await Alarm.init(showDebugLogs: true);

  await Firebase.initializeApp();
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  runApp(
    MaterialApp(
      theme: ThemeData(useMaterial3: false),
      home: const ExampleAlarmHomeScreen(),
    ),
  );
}

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  print("Executing background alarm");
  WidgetsFlutterBinding.ensureInitialized();
  await Alarm.init(showDebugLogs: true);
  AlarmService._triggerAlarm();
}

class AlarmService {

  static void _triggerAlarm() {
    final alarmSettings = AlarmSettings(
      id: DateTime.now().millisecondsSinceEpoch % 10000,
      dateTime: DateTime.now(),
      assetAudioPath: 'assets/marimba.mp3',
      volume: 0.5,
      vibrate: true,
      notificationTitle: 'Alarm example',
      notificationBody: 'Shortcut button alarm with delay of zero hours',
    );

    Alarm.set(alarmSettings: alarmSettings);
  }
}

home.dart

@override
  void initState() {
    super.initState();
    AlarmPermissions.checkNotificationPermission();
    if (Alarm.android) {
      AlarmPermissions.checkAndroidScheduleExactAlarmPermission();
    }
    loadAlarms();
    ringSubscription ??= Alarm.ringStream.stream.listen(navigateToRingScreen);
    updateSubscription ??= Alarm.updateStream.stream.listen((_) {
      loadAlarms();
    });
  }

So, should i change the initialization of the ringstream listener and put it also in my _firebaseMessagingBackgroundHandler too? How can i do that correctly to keep the other alarm functionality?

I haven't worked much with streams or background services, so any help would be much appreciated!😁, Thank you very much!

gdelataillade commented 3 weeks ago

Hi @IkerCasillass,

It’s been a while since I last used Firebase Messaging, so I have a few quick questions:

I might be wrong but if I recall correctly, you might need to add @pragma('vm:entry-point') at the top of the _firebaseMessagingBackgroundHandler method.

IkerCasillass commented 3 weeks ago

Hi, thank you for taking the time to answer my question.

Yes, _firebaseMessagingBackgroundHandler is triggered and i can see the "Executing background alarm" print.

I don't know if this also fails in IOS because i implemented different logic, receiving the message in the native side and invoking a method via MethodChannel. But i wasn't able to replicate this logic in Android, so i'm trying to do it in the flutter side with this FirebaseMessaging.onBackgroundMessage

Thank you for pointing out the lack of @pragma('vm:entry-point'), i added it but it didn't resolve the issue.

When the function is called via FirebaseMessaging.onBackgroundMessage, the sound and all the other alarm flow is triggered. But the ring screen isn't shown due to the ringStream listener not being set at the moment.