flutter_background_service not receiving updates

2.7k views Asked by At

I'm using awesome_notifications and flutter_background_service in conjunction to update some app state when receiving data notifications from FirebaseMessaging. As noted in the awesome_notifications, the background message handler must be a top-level function, so I am using flutter_background_service to pass data to the main isolate and update app state.

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initializeBackgroundService();
  FirebaseMessaging.onBackgroundMessage(_backgroundMessageHandler);
  _initLocalNotifications();
  runApp(MyApp());
}

I'm initializing the background service similarly to the example in flutter_background_service:

Future<void> initializeBackgroundService() async {
  final service = FlutterBackgroundService();
  await service.configure(
    androidConfiguration: AndroidConfiguration(
      onStart: onStart,
      autoStart: true,
      isForegroundMode: true,
    ),
    iosConfiguration: IosConfiguration(
      autoStart: true,
      onForeground: onStart,
      onBackground: onIosBackground,
    ),
  );
  await service.startService();
}

and invoking update in the _backgroundMessageHandler when a notification is received:

Future<void> _backgroundMessageHandler(
  RemoteMessage message,
) async {
  final service = FlutterBackgroundService();

  ...

  service.invoke('update', {
    'key1': 'val1',
    'key2': 'val2',
  });
}

And in the StatefulWidget for my app in the main isolate, I'm listening on the update call to receive the data:

void listenForNotificationData() {
  final backgroundService = FlutterBackgroundService();
  backgroundService.on('update').listen((event) async {
    print('received data message in feed: $event');
  }, onError: (e, s) {
    print('error listening for updates: $e, $s');
  }, onDone: () {
    print('background listen closed');
  });
}

It's never invoking the listen callback on the 'update' event. I can confirm it's calling the invoke('update') portion and calling on('update').listen, but never receiving the update. It also doesn't seem to be erroring out. Am I missing a step somewhere here?

2

There are 2 answers

1
Zorkan Erkan On

Firstly create a new class.

class ProjectBackgroundService{}

Now let's define the functions that you will use in this class.

The first of these will be the readyForShared() function. This function allows us to define and read local variables.

`Future<void> readyForShared() async {
   var sharedPreferences = await SharedPreferences.getInstance();
   counterValue = sharedPreferences.getString("yourVariable") ??"0";
 }`

Now let's write the function that saves our local variables.

Future<void> saveData(String value) async {
  var sharedPreferences = await SharedPreferences.getInstance();
  sharedPreferences.setString("yourVariable", value);
}

Now let's write your service function onStart()

@pragma('vm:entry-point')
void onStart(ServiceInstance service) async {

  // Only available for flutter 3.0.0 and later
  DartPluginRegistrant.ensureInitialized();

  // For flutter prior to version 3.0.0
  // We have to register the plugin manually

  SharedPreferences preferences = await SharedPreferences.getInstance();
  await preferences.setString("hello", "world");

  /// OPTIONAL when use custom notification
  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

  if (service is AndroidServiceInstance) {
    service.on('setAsForeground').listen((event) {
      service.setAsForegroundService();
    });

    service.on('setAsBackground').listen((event) {
      service.setAsBackgroundService();
    });
  }

  service.on('stopService').listen((event) {
    service.stopSelf();
  });
    
  // bring to foreground
  Timer.periodic(const Duration(seconds: 1), (timer) async {
    final receivePort = ReceivePort();
    // here we are passing method name and sendPort instance from ReceivePort as listener
    await Isolate.spawn(computationallyExpensiveTask, receivePort.sendPort);

    if (service is AndroidServiceInstance) {
      if (await service.isForegroundService()) {
        //It will listen for isolate function to finish
         receivePort.listen((sum) {
         flutterLocalNotificationsPlugin.show(
         888,
         'Title',
         'Description ${DateTime.now()}',
         const NotificationDetails(
         android: AndroidNotificationDetails(
         'my_foreground',
         'MY FOREGROUND SERVICE',
         icon: 'ic_bg_service_small',
         ongoing: true,
         ),
         ),
         );
         });

        var sharedPreferences = await SharedPreferences.getInstance();
        await sharedPreferences.reload(); // Its important
        service.setForegroundNotificationInfo(
          title: "My App Service",
          content: "Updated at ${sharedPreferences.getString("yourVariable") ?? 'no data'}",
        );
      }
    }


    /// you can print
    //if (kDebugMode) {
    //}

    // test using external plugin
    final deviceInfo = DeviceInfoPlugin();
    String? device;
    if (Platform.isAndroid) {
      final androidInfo = await deviceInfo.androidInfo;
      device = androidInfo.model;
    }

    if (Platform.isIOS) {
      final iosInfo = await deviceInfo.iosInfo;
      device = iosInfo.model;
    }
    service.invoke(
      'update',
      {
        "current_date": '400',
        "device": device,
      },
    );
  });
}

Now let's call our service

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
      readyForShared();
      ProjectBackgroundService().onStart();
    });
  }
0
Nguthiru On

I was encountering the same issue on flutter background service. I solved it by removing the async keyword from the callback and creating a separate async function to perform the callback operations.

void listenForNotificationData() {
  final backgroundService = FlutterBackgroundService();
  backgroundService.on('update').listen((event) {
    print('received data message in feed: $event');
  }, onError: (e, s) {
    print('error listening for updates: $e, $s');
  }, onDone: () {
    print('background listen closed');
  });
}

void action(Map? event) async {
print('received data message in feed: $event');
}

Hope it helps, forgive me if there are syntax error