skip to Main Content

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

2

Answers


  1. 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.

    Login or Signup to reply.
  2. 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(),
        );
      }
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search