flutterdartbackground-taskflutter-local-notificationflutter-workmanager

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


Solution

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