awesome_notifications icon indicating copy to clipboard operation
awesome_notifications copied to clipboard

How to handle button click event if an app was terminated?

Open alexursul opened this issue 2 years ago • 13 comments

Hi. I'm sorry if I missed it in the documentation.

I've created an action stream listener and it works great if an app in the background or foreground:

AwesomeNotifications().actionStream.listen((receivedNotification) async {

   if (receivedNotification.buttonKeyPressed == 'complete') {
      ... some important stuff

      if (receivedNotification.id != null) {
          AwesomeNotifications().dismiss(receivedNotification.id!);
      }
   }

}

When I kill the app I still receive notifications, however my listener doesn't work. My button with buttonType: ActionButtonType.KeepOnTop doesn't hide the notification and, more important, does nothing. Is it possible to fix such behaviour?

alexursul avatar Jul 29 '21 13:07 alexursul

Did you get a momentary solution?

clebemachado avatar Jul 31 '21 01:07 clebemachado

Did you get a momentary solution?

Unfortunately no, I've disabled notification actions until a solution is found.

alexursul avatar Jul 31 '21 07:07 alexursul

Did you get a momentary solution?

Unfortunately no, I've disabled notification actions until a solution is found.

i did succeed to do it, but it kind tricky beacuse we need to called static function same as firebase messages(in background) if @rafaelsetragni will want i will do fork to it i would discuss with him(for now i did it only for android, ios not my strong side).

saharvx9 avatar Aug 03 '21 11:08 saharvx9

i did succeed to do it, but it kind tricky beacuse we need to called static function same as firebase messages(in background) if @rafaelsetragni will want i will do fork to it i would discuss with him(for now i did it only for android, ios not my strong side).

For android only is also good, could you please share your solution here?

alexursul avatar Aug 03 '21 12:08 alexursul

i did succeed to do it, but it kind tricky beacuse we need to called static function same as firebase messages(in background) if @rafaelsetragni will want i will do fork to it i would discuss with him(for now i did it only for android, ios not my strong side).

For android only is also good, could you please share your solution here?

Here is my pull request: https://github.com/saharvx9/awesome_notifications/tree/background_click the code not looking so good, but the implementation in flutter for who want to use very simple: sample:

  _initNotificationReceiver(){
    AwesomeNotifications().initialize(
      // set the icon to null if you want to use the default app icon
        null,
        [
          NotificationChannel(
              channelKey: NotificationChannel.CHANNEL_PROGRESS_KEY,
              channelName: NotificationChannel.CHANNEL_PROGRESS_NAME,
              channelDescription: NotificationChannel.CHANNEL_PROGRESS_DESCRIPTION,
              defaultColor: Colors.blue,
              ledColor: Colors.white
          )
        ],
      backgroundClickAction: _observeActionStream
    );
  }
  //must be top function or static
  _observeActionStream(ReceivedAction action) async {
    print(" handle click BACKGROUND: $action");
  }

Notice for this fix: The notification action button MUST be keepOnTop

in addition i saw @rafaelsetragni working on it too: https://github.com/rafaelsetragni/awesome_notifications/tree/notifications-isolates i did check it but it did not work so i did something quickly for fixing it until he finished :)

saharvx9 avatar Aug 03 '21 13:08 saharvx9

After thinking a lot about it, I propose a change to the plugin architecture:

  • Deprecate all stream listeners and replace then by static methods in the main application class.

For example, instead of doing this:

    AwesomeNotifications().createdStream.listen((receivedNotification) {
        // created code goes here
    });

    AwesomeNotifications().displayedStream.listen((receivedNotification) {
        // displayed code goes here
    });

    AwesomeNotifications().dismissedStream.listen((receivedNotification) {
        // dismissed code goes here
    });

    AwesomeNotifications().actionStream.listen((receivedNotification) {
        // action code goes here
    });

... now we gonna do this:

class App extends StatefulWidget {

  App();

  static String name = 'Awesome Notifications - Example App';
  static Color mainColor = Color(0xFF9D50DD);

  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  /// Use this method to detect when a new notification or a schedule is created
  static Future <void> onCreatedNotificationMethod(ReceivedNotification receivedNotification) async {
    // created code goes here
  }

  /// Use this method to detect every time that a new notification is displayed
  static Future <void> onDisplayedNotificationMethod(ReceivedNotification receivedNotification) async {
    // displayed code goes here
  }

  /// Use this method to detect if the user dismissed a notification
  static Future <void> onDismissedNotificationMethod(ReceivedAction receivedAction) async {
    // dismissed code goes here
  }

  /// Use this method to detect when the user taps on a notification or action button
  static Future <void> onActionNotificationMethod(ReceivedAction receivedAction) async {

    // action code goes here

    // Redirects to the desired page without need to use the local context object
    App.navigatorKey.currentState?.pushNamed('NotificationDetailsPage');
  }

  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {

  @override
  void initState() {

    // Only after set at least one method, the notification's events are delivered
    AwesomeNotifications().setListeners(
        onCreatedNotificationMethod:   App.onCreatedNotificationMethod,
        onDisplayedNotificationMethod: App.onDisplayedNotificationMethod,
        onActionNotificationMethod:    App.onActionNotificationMethod,
        onDismissedNotificationMethod: App.onDismissedNotificationMethod
    );

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // The navigator key is necessary to allow to navigate through static classes
      navigatorKey: App.navigatorKey,

      // These routes are declared in a separated file called routes.dart
      initialRoute: PAGE_HOME,
      routes: materialRoutes,

      title: App.name,
      color: App.mainColor,

      builder: (context, child) => MediaQuery(
        data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
        child: child ?? const SizedBox.shrink(),
      ),

      theme: ThemeData(
        brightness: Brightness.light,

        primaryColor: App.mainColor,
      ),
    );
  }
}

That way everything will be easier. The displayed, created, dismissed, and action methods can be called at any time, there's no more issues with streams being called twice, and everyone can re-implement the observer pattern on their own if they want.

What do you think of this? Do you agree or not?

rafaelsetragni avatar Aug 14 '21 16:08 rafaelsetragni

I think this is a great idea. There are packages that have a similar way of callbacks implementation (background_fetch, workmanager) and it works fine.

alexursul avatar Aug 15 '21 06:08 alexursul

Late to the conversation. Running into this issue myself now. Is the problem that we can't support listening to streams after the app was terminated, but callbacks are fine?

I see the proposed solution isn't in the latest version of the library yet.

irjayjay avatar Mar 16 '22 11:03 irjayjay

Yes, it is available. It's the 0.7.0 beta version.

rafaelsetragni avatar Mar 20 '22 10:03 rafaelsetragni

I would like to have a keep on top action button where the user hits submit and in the background the app makes a web request. If successful I then display a confirmation notification, if failed I would display a failure one with the default button type to have the user handle the failure in the app. This action typically happens one/twice a week so I believe I'm having a similar problem where if the app is killed nothing happens. Would these changes allow keep on top to function even when the app is killed or should I just switch to default type for all notifications? I imagine this could be a limitation with Android?

jmccaull avatar Apr 16 '22 00:04 jmccaull

So I've been playing around with the 0.7.0 beta, I've switched from keep on top to the new SilentBackgroundAction type. Everything seems to be working greats so far. I've had to add "DartPluginRegistrant.ensureInitialized();" to my callback, it looks like you had this commented out. Making everything static makes testing a little bit wonkier but that is probably unavoidable. Thanks again for the awesome work!

jmccaull avatar Apr 17 '22 19:04 jmccaull

I've had to add "DartPluginRegistrant.ensureInitialized();" to my callback

Hi @jmccaull, can you please tell where can I put it? I tried to put it in the listener onActionReceivedMethod function but it doesn't work

minhdanh avatar Jun 26 '22 11:06 minhdanh

() => AnyWidget(),

or

(){
   AnyWidget();
}

are not static or global functions. They are dynamic functions created at runtime. To use static functions or global functions on dart, please check the example at Awesome Notifications first steps

rafaelsetragni avatar Jun 28 '22 18:06 rafaelsetragni

Hi! Sorry for taking so long to respond to your issue on GitHub. I was focused on releasing the new 0.7.0 version of awesome_notifications and awesome_notifications_fcm and i didn't had time enough to do both. But now I can answer all your questions.

So now i'm asking you to recreate this topic using the new issue template. There's a lot of missing informations that i need to understand your problem, and all the instructions are already in the new issue template. Also, remember to check beforehand if your issue was posted by another user.

So I will automatically close all previous issues so far. Sorry for the inconvenience and i will be waiting for your new issue request.

Thank you so much for your support!

rafaelsetragni avatar Sep 29 '22 18:09 rafaelsetragni