Flutter Local Notification not working in background?
Asked Answered
O

6

13

I am using flutter local notification to display schedule notification in my app. But unfortunately it is not working when app is in terminated state.

Here is my code:

class Notifications {
static final FlutterLocalNotificationsPlugin _notifications =
  FlutterLocalNotificationsPlugin();

static Future<NotificationDetails> _notificationDetails() async {
return const NotificationDetails(
    android: AndroidNotificationDetails(
      'weekly notification channel id',
      'weekly notification channel name',
      channelDescription: 'weekly notification description',
      playSound: true,
      sound: RawResourceAndroidNotificationSound('azan1'),
      importance: Importance.high,
    ),
    iOS: IOSNotificationDetails(sound: 'azan1.mp3', presentSound: true));
}

static void init() async {
tz.initializeTimeZones();
const AndroidInitializationSettings android =
    AndroidInitializationSettings('@mipmap/ic_launcher');
const IOSInitializationSettings iOS = IOSInitializationSettings();
InitializationSettings settings =
    const InitializationSettings(android: android, iOS: iOS);
await _notifications.initialize(settings);
final String locationName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(locationName));
}

static void showScheduledNotification(
int id, {
required DateTime scheduledDate,
String? title,
String? body,
String? payload,
}) async {
await _notifications.zonedSchedule(
  id,
  'Azan Time',
  '$body Prayer Time',
  _scheduleDaily(
      Time(scheduledDate.hour, scheduledDate.minute, scheduledDate.second)),
  await _notificationDetails(),
  androidAllowWhileIdle: true,
  uiLocalNotificationDateInterpretation:
      UILocalNotificationDateInterpretation.absoluteTime,
  matchDateTimeComponents: DateTimeComponents.time,
  payload: payload,
 );
 }

static tz.TZDateTime _scheduleDaily(Time time) {
tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime schdeuledDate = tz.TZDateTime(tz.local, now.year, now.month,
    now.day, time.hour, time.minute, time.second);
return schdeuledDate.isBefore(now)
    ? schdeuledDate.add(const Duration(days:1))
    : schdeuledDate;
}

static Future<void> cancelNotification(int id) async {
await _notifications.cancel(id);
}

static Future<void> cancelAllNotifications() async {
await _notifications.cancelAll();
 }
 }

I have also added all properties in Android.xml file. But still it is not working if anybody know the solution of this problem kindly answer this question.

Oresund answered 3/4, 2022 at 10:9 Comment(5)
I have the same. Did you solved your problem?Healthy
No Bro, not solved till now.Oresund
I am facing the same problem. Have done everything right. It triggers if i schedule it +1 minute. But as soon as set it for more than that it doesn't trigger.Cubital
@AmmarZahid the minimum is 15 minutesTedman
try checking app in release modeWilmoth
H
4

Local notifications might be a bit tricky. Look at the flutter_local_notifications README file:

Some Android OEMs have their own customised Android OS that can prevent applications from running in the background. Consequently, scheduled notifications may not work when the application is in the background on certain devices (e.g. by Xiaomi, Huawei). If you experience problems like this then this would be the reason why. As it's a restriction imposed by the OS, this is not something that can be resolved by the plugin. Some devices may have setting that lets users control which applications run in the background. The steps for these can vary but it is still up to the users of your application to do given it's a setting on the phone itself.

It has been reported that Samsung's implementation of Android has imposed a maximum of 500 alarms that can be scheduled via the Alarm Manager API and exceptions can occur when going over the limit.

Source: https://pub.dev/packages/flutter_local_notifications#scheduled-android-notifications

Hydroquinone answered 7/2, 2023 at 14:34 Comment(0)
C
3

According to my understanding which may be incorrect, working in the background and terminated are two completely different things.

If the app is terminated i.e. forced to quit and completely dead, there is no way to show local notifications, firebase push notifications should be used instead.

More info about AppLifecycleState.

Also, How do I check if the Flutter application is in the foreground or not?

Corbet answered 17/6, 2023 at 13:49 Comment(0)
E
1

If you could provide your main function, it would have been helpful. I'll give you a general example of how to create any scheduled notification.

import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:rxdart/rxdart.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

class NotificationApi {
  static final _notification = FlutterLocalNotificationsPlugin();
  static final onNotifications = BehaviorSubject<String?>();

  static Future _notificationDetails() async {
    return const NotificationDetails(
      android: AndroidNotificationDetails(
        'channel id',
        'channel name',
        channelDescription: 'Update users on new deal',
        importance: Importance.max,
        enableLights: true,
      ),
      iOS: IOSNotificationDetails(),
    );
  }

  static Future init({bool initScheduled = true}) async {
    const android = AndroidInitializationSettings('@drawable/ic_notcion');
    const iOS = IOSInitializationSettings();
    const settings = InitializationSettings(android: android, iOS: iOS);

    /// when app is closed
    final details = await _notification.getNotificationAppLaunchDetails();
    if (details != null && details.didNotificationLaunchApp) {
      onNotifications.add(details.payload);
    }
    await _notification.initialize(
      settings,
      onSelectNotification: (payload) async {
        onNotifications.add(payload);
      },
    );

    if(initScheduled){
      tz.initializeTimeZones();
      final locationName = await FlutterNativeTimezone.getLocalTimezone();
      tz.setLocalLocation(tz.getLocation(locationName));
    }
  }

  static tz.TZDateTime _scheduledDaily(Time time) {
    final now = tz.TZDateTime.now(tz.local);
    final scheduledDate = tz.TZDateTime(tz.local, now.year, now.month, now.day,
        time.hour);

    return scheduledDate.isBefore(now)
        ? scheduledDate.add(const Duration(days: 1))
        : scheduledDate;
  }

  static tz.TZDateTime _scheduleWeekly(Time time, {required List<int> days}) {
    tz.TZDateTime scheduledDate = _scheduledDaily(time);

    while (!days.contains(scheduledDate.weekday)) {
      scheduledDate = scheduledDate.add(const Duration(days: 1));
    }
    return scheduledDate;
  }

  static Future showWeeklyScheduledNotification({
    int id = 8,
    String? title,
    String? body,
    String? payload,
    required DateTime scheduledDate,
  }) async =>
      _notification.zonedSchedule(
        id,
        title,
        body,
        _scheduleWeekly(const Time(17), days: [
          DateTime.tuesday,
          DateTime.friday,
          DateTime.saturday,
          DateTime.sunday,
        ]),
        // tz.TZDateTime.from(scheduledDate, tz.local),
        await _notificationDetails(),
        payload: payload,
        androidAllowWhileIdle: true,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
        matchDateTimeComponents: DateTimeComponents.dayOfWeekAndTime,
      );

  static void cancelAll() => _notification.cancelAll();
}

On the main function, init the NotificationApi as follows:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
    //__________________________________Notification and Time Zone
  tz.initializeTimeZones();
  await NotificationApi.init();
  await NotificationApi.init(initScheduled: true);
}
 

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // This widget is the root of your application.

  ///_____________________Init state initialising notifications
  @override
  void initState() {
    loadAllNotifications;
    super.initState();
  }

  loadAllNotifications() {
    NotificationApi.showWeeklyScheduledNotification(
        title: '🏆 New Deals Available 🏆',
        body: '✨ Don\'t miss your opportunity to win BIG 💰💰',
        scheduledDate: DateTime.now().add(const Duration(seconds: 12)));
    NotificationApi.init(initScheduled: true);
    listenNotifications();
  }


  //___________________________Listen to Notifications
  void listenNotifications() =>
      NotificationApi.onNotifications.stream.listen(onClickedNotification);

  //__________________________On Notification Clicked
  void onClickedNotification(String? payload) => Navigator.of(context).push(
      MaterialPageRoute(builder: (context) => AppWrapper(updates: updates)));

  //________________________________________Widget Build
  @override
  Widget build(BuildContext context) => MaterialApp(
            debugShowCheckedModeBanner: false,
            title: 'Alpha Deals',
            home: MyHomePage()
          
      );
}
Enclave answered 19/2, 2023 at 17:12 Comment(5)
Does the weekly scheduling for multiple days work for you? I have the code exactly like you, but it always shows the notification only on the first day specified in the list (DateTime.tuesday in your case). The other days (DateTime.friday, DateTime.saturday, DateTime.sunday in your case) get ignored.Nevarez
Make sure the notification does not cancel, remove the code to cancel the notifications "static void cancelAll() => _notification.cancelAll();" and try again.Enclave
I did, still no luck. The _scheduleWeekly() Method does not seem to work for more than 1 day. Does it work for you?Nevarez
Yes. Mine works perfectly. Follow the instructions in this video. youtube.com/watch?v=bRy5dmts3X8&t=601s. If you find the error in the code, I will appreciate your response.Enclave
The function in the video only returns one DateTime object, which is customized based on the first day in the list that you provide with the function. But still, it is only one DateTime object, so it will only schedule a notification for this specific day.Nevarez
H
0

In your Application.java if this part not work you can escape it to run the app .

private void createNotificationChannels() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channelHigh = new NotificationChannel(
                    CHANNEL_HIGH,
                    "Notificación Importante",
                    NotificationManager.IMPORTANCE_HIGH
            );

            channelHigh.setDescription("Canal Alto");

            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channelHigh);

        }
    }

Check the Manifest File and do not forget to add

  <meta-data
    android:name="com.google.firebase.messaging.default_notification_channel_id"
    android:value="default"/>

For More Check This Link

Hogg answered 7/2, 2023 at 5:43 Comment(0)
A
0

i think you need to init it again in the main background service ( like workmanager ) when using it with the app close you need to init most of the packages you use in the back before using it in the background (its a separated environment)

Acerate answered 3/3 at 22:34 Comment(2)
for example: // Initialization FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); var initializationSettingsAndroid = AndroidInitializationSettings('app_icon'); var initializationSettings = InitializationSettings(android: initializationSettingsAndroid); await flutterLocalNotificationsPlugin.initialize(initializationSettings);Acerate
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Shipper
S
0

Did you initialised it in your main.dart ?

@pragma("vm:entry-point")
Future<void> handler(RemoteMessage message) async => NotificationManager.instance.monitorBackground(message);

And also add this method :

  void monitorBackground(RemoteMessage message) async {
    try {
      await Firebase.initializeApp();

      final Map<String, dynamic> data = message.data;
      String? notificationType = (data["category"] ?? "").toString().toLowerCase();
      if (notificationType == "order-status-update" || notificationType == "order-update") {
        _publishOrderUpdate(message, public: true);
      } else if (notificationType == "campaign" || notificationType == "notice") {
        // _publish(message, public: true);
      }
    } catch (error) {
      debugPrintStack();
    }

}

Stgermain answered 8/5 at 4:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.