Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show flutter local notification when firebase notification received in foreground and background

I am trying to show flutter local notification when receiving firebase notification in background, foreground and terminated states of app. i am trying to resolve this using work manager package and it is working like a charm in android. it works in foreground, background and terminated states as well, but it is not working in ios device. if i am trying to execute task which is not registered in info plist and appDelegate it gives me an error but after registering it is not doing anything (no error and not executing my code as well). i imagine that my task is registered but iOS did not execute it in some reason. if anyone has done something similar, please provide some examples or maybe some thoughts about what i am doing wrong?

Trying to resolve problem using work manager and flutter local notifications

like image 284
Karen Ghazaryan Avatar asked Sep 03 '25 16:09

Karen Ghazaryan


2 Answers

I struggled with this issue for many weeks until I managed to have notifications appear for my ios app regardless of its state.

First, you must tick 'Background fetch' and 'Background processing' modes in Xcode. Those can be found under Signing & Capabilities.

enter image description here

Next, you must register your tasks and give your app notifications capabilities in your AppDelegate.swift.

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    UNUserNotificationCenter.current().delegate = self

    WorkmanagerPlugin.setPluginRegistrantCallback { registry in
      // Registry, in this case is the FlutterEngine that is created in Workmanager's
      // performFetchWithCompletionHandler or BGAppRefreshTask.
      // This will make other plugins available during a background operation.
      GeneratedPluginRegistrant.register(with: registry)
    }

    // you will need to duplicate the line below for each identifier
    WorkmanagerPlugin.registerTask(withIdentifier: "your.task.identifier")

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  override func userNotificationCenter(
      _ center: UNUserNotificationCenter,
      willPresent notification: UNNotification,
      withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler(.alert) // shows banner even if app is in foreground
    }

}

You will need to also register your task in the info.plist as so:

    <key>BGTaskSchedulerPermittedIdentifiers</key>
    <array>
        <string>your.task.identifier/string>
    </array>
    <key>UIBackgroundModes</key>
    <array>
        <string>fetch</string>
        <string>processing</string>
    </array>

registerPeriodicTask doesn't work for ios, so instead, you should use the flutter_local_notifications periodicallyShow function.

For reference, I provided a snippet from my code that shows how I am using workmanager and flutter_local_notifications;

void registerWorkmanagerTask() async {
  try {
    await Workmanager().registerOneOffTask(
      AppConstants.notifyReviewTask,
      AppConstants.notifyReviewTask,
      initialDelay: Duration(hours: 4),
      existingWorkPolicy: ExistingWorkPolicy.replace,
      backoffPolicy: BackoffPolicy.linear,
    );
  } catch (e) {
    print("exception caught $e");
  }
}

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((taskName, _) async {
    try {
      await NotificaitonService().setup();
      if (taskName == AppConstants.notifyReviewTask)
        await NotificaitonService().showNotification(
          body: 'items are ready to be reviewed',
          notificationId: AppConstants.recallNotificationId,
          channel: AppConstants.recallChannel,
          title: 'Time to review',
        );
      return Future.value(true);
    } catch (e) {
      print("exception caught $e");
    }
  });
}

Remember that ios devices can decide to postpone running your background work manager task for many reasons (e.g. low battery). Moreover, notifications won't be displayed using an ios simulator.

like image 129
Firas AT Avatar answered Sep 05 '25 05:09

Firas AT


You can do this by using flutter_local_notification, If you are open to it. It is much simpler and compact.

Pre-requesites:

  1. Have following packages setup:
    firebase_core
    firebase_messaging 
    flutter_local_notifications
    
  2. Notification for background and terminated state is handled by flutter app, default notification service
  3. Notification for foreground should be created, here we create using flutter_local_notification listening to the fcm stream.

Complete Code:

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:hello/notification_helper.dart';

Future<void> _firebaseMessagingBackgroundHandler(message) async {
  await Firebase.initializeApp();
  print('Handling a background message ${message.messageId}');
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  final fcmToken = await FirebaseMessaging.instance.getToken();
  debugPrint(fcmToken);
  FirebaseMessaging messaging = FirebaseMessaging.instance;

  NotificationSettings settings = await messaging.requestPermission(
    alert: true,
    announcement: false,
    badge: true,
    carPlay: false,
    criticalAlert: false,
    provisional: false,
    sound: true,
  );

  if (settings.authorizationStatus == AuthorizationStatus.authorized) {
    print('User granted permission');
  } else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
    print('User granted provisional permission');
  } else {
    print('User declined or has not accepted permission');
  }
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  runApp(const MyApp());
}

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

  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    LocalNotification.initialize();
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      LocalNotification.showNotification(message);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
      child: Text("Hello"),
    ));
  }
}

notification_helper.dart// class explicitly to handle foreground notification

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

class LocalNotification {
  static final FlutterLocalNotificationsPlugin _notiPlugin =
      FlutterLocalNotificationsPlugin();

  static void initialize() {
    final InitializationSettings initialSettings = InitializationSettings(
      android: AndroidInitializationSettings(
        '@mipmap/ic_launcher',
      ),
    );
    _notiPlugin.initialize(initialSettings,
        onDidReceiveNotificationResponse: (NotificationResponse details) {
      print('onDidReceiveNotificationResponse Function');
      print(details.payload);
      print(details.payload != null);
    });
  }

  static void showNotification(RemoteMessage message) {
    final NotificationDetails notiDetails = NotificationDetails(
      android: AndroidNotificationDetails(
        'com.example.push_notification',
        'push_notification',
        importance: Importance.max,
        priority: Priority.high,
      ),
    );
    _notiPlugin.show(
      DateTime.now().microsecond,
      message.notification!.title,
      message.notification!.body,
      notiDetails,
      payload: message.data.toString(),
    );
  }
}

like image 43
krishnaacharyaa Avatar answered Sep 05 '25 05:09

krishnaacharyaa