Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onDidReceiveNotificationResponse DID NOT TRIGGER WHEN APP IS IN FOREGROUD #2390

Open
Abdulah-butt opened this issue Aug 19, 2024 · 8 comments

Comments

@Abdulah-butt
Copy link

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sos_cameroon/app/common/shared_preference_manager.dart';

import 'package:sos_cameroon/src/features/notifications/data/repositories/local_notification_service.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

import '../../models/schedule_notification_model.dart';

@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
  // ignore: avoid_print
  print('notification(${notificationResponse.id}) action tapped: '
      '${notificationResponse.actionId} with'
      ' payload: ${notificationResponse.payload}');
  if (notificationResponse.input?.isNotEmpty ?? false) {
    // ignore: avoid_print
    print(
        'notification action tapped with input: ${notificationResponse.input}');
  }
}

class FlutterLocalNotification implements LocalNotificationService {
  final SharedPreferences _sharedPreferences =
      SharedPreferencesManager.instance;
  final StreamController<String?> _selectNotificationStream =
      StreamController<String?>.broadcast();

  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();

  final AndroidNotificationDetails _channel = const AndroidNotificationDetails(
      'default_notification_channel_id', // id
      'High Importance Notifications', // title
      // description
      importance: Importance.high,
      enableLights: true,
      playSound: true);

  @override
  Future<void> initialize() async {
    await _configureLocalTimeZone();
    await _configureNotifications();
  }

  Future<bool> _isAlreadyScheduled() async {
    return _sharedPreferences.getBool("local_notification_schedule") ?? false;
  }

  Future<bool> _localNotificationScheduled() async {
    return _sharedPreferences.setBool("local_notification_schedule", true);
  }

  Future<void> _configureNotifications() async {
    const AndroidInitializationSettings initializationSettingsAndroid =
        AndroidInitializationSettings('@mipmap/ic_launcher');
    final DarwinInitializationSettings initializationSettingsDarwin =
        DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
      onDidReceiveLocalNotification:
          (int id, String? title, String? body, String? payload) async {},
    );
    final InitializationSettings initializationSettings =
        InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsDarwin,
      macOS: initializationSettingsDarwin,
    );

    await flutterLocalNotificationsPlugin.initialize(
      initializationSettings,
      onDidReceiveNotificationResponse:onNotificationTap,
      onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
    );

  }

// on tap on any notification
  static void onNotificationTap(NotificationResponse notificationResponse) {
    // _selectNotificationStream.add(notificationResponse.payload!);
    debugPrint("NOTIFICATION IS TAPPED +++++++++++");
  }

  @override
  Future<String?> notificationPayloadFromWhichAppOpened() async {
    final NotificationAppLaunchDetails? notificationAppLaunchDetails =
        !kIsWeb && Platform.isLinux
            ? null
            : await flutterLocalNotificationsPlugin
                .getNotificationAppLaunchDetails();
    if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
      String? selectedNotificationPayload =
          notificationAppLaunchDetails!.notificationResponse?.payload;
      return selectedNotificationPayload;
    }
    return null;
  }

  @override
  Future<void> requestPermissions() async {
    await _requestPermissions();
  }

  @override
  Future<void> scheduleNotification(
      {required int id,
      required String title,
      required String description,
      String? payload,
      required Duration durationFromCurrentTime}) async {
    debugPrint(
        "NOTIFICATION SCHEDULED AT+++++++ $durationFromCurrentTime ++++ ${tz.TZDateTime.now(tz.local).add(durationFromCurrentTime)}");
    await flutterLocalNotificationsPlugin.zonedSchedule(
        id,
        title,
        description,
        payload: payload,
        tz.TZDateTime.now(tz.local).add(durationFromCurrentTime),
        NotificationDetails(android: _channel),
        androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime);
  }

  Future<void> _configureLocalTimeZone() async {
    if (kIsWeb || Platform.isLinux) {
      return;
    }
    tz.initializeTimeZones();
    final String timeZoneName = await FlutterTimezone.getLocalTimezone();
    tz.setLocalLocation(tz.getLocation(timeZoneName));
  }

  Future<void> _requestPermissions() async {
    if (Platform.isIOS || Platform.isMacOS) {
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              IOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(
            alert: true,
            badge: true,
            sound: true,
          );
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              MacOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(
            alert: true,
            badge: true,
            sound: true,
          );
    } else if (Platform.isAndroid) {
      final AndroidFlutterLocalNotificationsPlugin? androidImplementation =
          flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>();
      final bool? grantedNotificationPermission =
          await androidImplementation?.requestNotificationsPermission();
      // If the app requires scheduling notifications with exact timings
      await AndroidFlutterLocalNotificationsPlugin()
          .requestExactAlarmsPermission();
    }
  }

  @override
  Future<void> scheduleAllNotificationsFromJson() async {
    // 1. FIRST CHECK IF NOTIFICATION ALREADY SCHEDULES OR NOT

    // if(await _isAlreadyScheduled()){
    //   debugPrint("NOTIFICATIONS ARE ALREADY SCHEDULED ++++++");
    //   return;
    // }

    List<ScheduleNotificationModel> scheduleNotifications =
        await _loadNotifications();
    for (int index = 0; index < scheduleNotifications.length; index++) {
      ScheduleNotificationModel notification = scheduleNotifications[index];
      LocaleData localeData = notification.locales!['en']!;
      scheduleNotification(
        id: index,
        title: localeData.title ?? "N/A",
        description: localeData.description ?? "N/A",
        payload: json.encode(notification.toJson()),
        durationFromCurrentTime: Duration(
          days: ((notification.day ?? 1) - 1),
          hours: notification.hour ?? 0,
          minutes: notification.minute ?? 0,
          seconds: notification.second ?? 0,
        ),
      );
    }
    _localNotificationScheduled();
  }

  /// Read json according to current locale
  Future<List<ScheduleNotificationModel>> _loadNotifications() async {
    // final  currentLocale = Localizations.localeOf(context);
    // debugPrint("CURRENT LOCALE : ${currentLocale.languageCode}");
    final jsonString =
        await rootBundle.loadString('assets/local_notifications/mock.json');
    final jsonData = json.decode(jsonString) as List<dynamic>;
    return jsonData
        .map((json) => ScheduleNotificationModel.fromJson(json))
        .toList();
  }

  @override
  Stream<String?> selectNotificationStream() {
    return _selectNotificationStream.stream;
  }
}

@Roshan-pcy
Copy link

i have face same issue did get any solution?

@AmirmohammadNemati
Copy link

i have this problem too

@ced1check
Copy link

Same problem here, running on Android, any solutions, are we missing some specific setup?
Will try iOS pretty soon.

@ced1check
Copy link

Worked-around using response here: #2315 (comment)

@MaikuB
Copy link
Owner

MaikuB commented Oct 3, 2024

For those that say that they are facing this issue, check to ensure you have done all the configuration mentioned in the readme. If you have done so and still face an issue, please provide a link to a repository hosting a minimal app that can reproduce the problem. This could be one you've created yourself or another way is to fork the repository and update the example app

Edit: please also mention the platform you encounter the issue on. If you provide a the repo, also include mention that Flutter version. Expectation is I should be able to clone the repository and reproduce the issue easily. Providing code snippets doesn't help as it's missing the native configuration

@itsatishay
Copy link

@MaikuB On tap not working for me as well, in IOS.

return await _flutterLocalNotificationsPlugin.initialize(
// ignore: prefer_const_constructors
InitializationSettings(
android: const AndroidInitializationSettings('@mipmap/ic_launcher'),
iOS: const DarwinInitializationSettings(onDidReceiveLocalNotification: handleForegroundPayload)),
onDidReceiveNotificationResponse: handlePayload,
);

Both the handleForegroundPayload or handlePayload never gets invoked when a user taps on the notification when app is in foreground.

Is there any specific IOS configuration that we need to do to handle on tap in IOS for foreground?
Do we need to create/add any notificationCategories during initialization to handle this?  

@MaikuB
Copy link
Owner

MaikuB commented Oct 22, 2024

@itsatishay the configuration needed is in the readme. You'll need to provide a link to repo hosting a minimal app that can reproduce the issue. I've asked in other threads that the community provides a link to a minimal repo hosting an app to reproduce the problem but haven't gotten a (favourable) response. This is the best way to identify if there is a legitimate issue or that developers haven't completed the expected configuration

@marcellocamara
Copy link

@Abdulah-butt could you provide a repository link of your example code for MaikuB investigate ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants