How to instantiate local notifications on Isolate?

1,547

There is a package you can use called flutter_isolate. According to its description, it allows the use of flutter plugins in a new isolate. I used it with flutter_ffmpeg and sqflite and it worked. When I tried to use them without this package I received the same error.

After adding the flutter_isolate package to your pubspec.yaml, instead of

Future f = compute(_isolateUpdateFunction, SharedVariables.appDocsDirPath);

You can use

Future f = FlutterIsolate.spawn(_isolateUpdateFunction, SharedVariables.appDocsDirPath);

The package will register all plugins automatically using the flutter generated file at app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java (on Android. I don't know how it works on iOS). But there are some issues on github (here and here) about plugins not working properly. So, according to the package creator, you can copy this file and create a custom plugin registrant deleting the plugins you don't need when starting a new FlutterIsolate. Then, in your MainActivity file, call the method setCustomIsolateRegistrant from FlutterIsolatePlugin.

For example:

public final class IsolatePluginRegistrant {
  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
    ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
    com.arthenica.flutter.ffmpeg.FlutterFFmpegPlugin
      .registerWith(shimPluginRegistry.registrarFor("com.arthenica.flutter.ffmpeg.FlutterFFmpegPlugin"));
  }
}
public class MainActivity extends FlutterActivity {
  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    FlutterIsolatePlugin.setCustomIsolateRegistrant(IsolatePluginRegistrant.class);
    super.onCreate(savedInstanceState);
  }
}

Have in mind I do not understand how native code works on Android. This example is inspired from an answer from one of the issues I mentioned. So, I'm not sure if this example works or even if it is correct, but this is the principle and I think it is a useful information. I hope it helps.

Share:
1,547
clicksrv
Author by

clicksrv

I am an individual who looks forward to developing my skills as I work. I have a positive outlook and can adjust to situations very well. I am very tech savvy, creative and ideative. Also, I love photography, graphic designing and travelling!

Updated on December 15, 2022

Comments

  • clicksrv
    clicksrv over 1 year

    Background of my problem: I am developing a Flutter app that performs a repeated background activity every few hours and notifies the user if there are any updates.

    Currently, I have created an isolate function that is able to perform the background activity (but is not currently scheduled) and I'm using the flutter_local_notifications package to generate the notifications locally.

    Here's the code I have:

    The piece that calls the isolate function-

    Future<void> executeBgUpdate() async {
      Completer c = Completer();
    
      ProductManager.close();
      Hive.close();
      Future f = compute(_isolateUpdateFunction, SharedVariables.appDocsDirPath); // See next block for code
    
      f.then((value) async {
        await init.db();
        c.complete(value);
      });
    
      return c.future;
    }
    

    The isolate function-

    Future<void> _isolateUpdateFunction(String appDataDirPath) async {
      Completer c = Completer();
    
      try {
        await init.logger();
        await init.db(appDocsDirPath: appDataDirPath);
        Notifier.init(); // See next block for code
    
        // Background Task goes here
    
        await Future.wait(futures);
    
        Notifier.showNotification();
    
        ProductManager.close();
        Hive.close();
      } catch (ex, stacktrace) {
        SharedInstances.logger.e("Failed in BG Update Isolate!", ex, stacktrace);
      }
    
      c.complete(null);
      return c.future;
    }
    

    Contents of Notifier.init()-

    static FlutterLocalNotificationsPlugin _notifier;
    
      static void init() {
        _notifier = new FlutterLocalNotificationsPlugin();
    
        var initializationSettingsAndroid =
        new AndroidInitializationSettings('@mipmap/ic_launcher');
    
        var initializationSettingsIOS = new IOSInitializationSettings(
            onDidReceiveLocalNotification: null);
    
        var initializationSettings = new InitializationSettings(
            initializationSettingsAndroid, initializationSettingsIOS);
    
        _notifier.initialize(initializationSettings,
            onSelectNotification: null);
      }
    

    The exception that I'm getting is:

    E/flutter (30954): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
    E/flutter (30954): If you're running an application and need to access the binary messenger before `runApp()` has been called (for example, during plugin initialization), then you need to explicitly call the `WidgetsFlutterBinding.ensureInitialized()` first.
    E/flutter (30954): If you're running a test, you can call the `TestWidgetsFlutterBinding.ensureInitialized()` as the first line in your test's `main()` method to initialize the binding.
    E/flutter (30954): #0      defaultBinaryMessenger.<anonymous closure> (package:flutter/src/services/binary_messenger.dart:73:7)
    E/flutter (30954): #1      defaultBinaryMessenger (package:flutter/src/services/binary_messenger.dart:86:4)
    E/flutter (30954): #2      MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:140:62)
    E/flutter (30954): #3      MethodChannel.setMethodCallHandler (package:flutter/src/services/platform_channel.dart:368:5)
    E/flutter (30954): #4      FlutterLocalNotificationsPlugin.initialize (package:flutter_local_notifications/src/flutter_local_notifications.dart:94:14)
    E/flutter (30954): <asynchronous suspension>
    

    If I add WidgetsFlutterBinding.ensureInitialized() to before the Notifier is initialized in the isolate, I get this exception-

    error: native function 'Window_setNeedsReportTimings' (2 arguments) cannot be found
    
    // StackTrace:
    I/flutter (  433): │ #0      Window.onReportTimings= (dart:ui/window.dart:964:7)
    I/flutter (  433): │ #1      SchedulerBinding.addTimingsCallback (package:flutter/src/scheduler/binding.dart:230:14)
    I/flutter (  433): │ #2      SchedulerBinding.initInstances (package:flutter/src/scheduler/binding.dart:206:7)
    I/flutter (  433): │ #3      PaintingBinding.initInstances (package:flutter/src/painting/binding.dart:21:11)
    I/flutter (  433): │ #4      SemanticsBinding.initInstances (package:flutter/src/semantics/binding.dart:22:11)
    I/flutter (  433): │ #5      RendererBinding.initInstances (package:flutter/src/rendering/binding.dart:29:11)
    I/flutter (  433): │ #6      WidgetsBinding.initInstances (package:flutter/src/widgets/binding.dart:253:11)
    I/flutter (  433): │ #7      new BindingBase (package:flutter/src/foundation/binding.dart:56:5)
    I/flutter (  433): │ #8      new _WidgetsFlutterBinding&BindingBase&GestureBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #9      new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #10     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #11     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #12     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #13     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #14     new _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #15     new WidgetsFlutterBinding (package:flutter/src/widgets/binding.dart)
    I/flutter (  433): │ #16     WidgetsFlutterBinding.ensureInitialized (package:flutter/src/widgets/binding.dart:1083:7)
    

    I'm quite clueless as to how I should proceed. Any help would be great, thanks!