How to register Flutter application plugins with a separate FlutterEngine in iOS

Viewed 919

So my flutter app's isolate function is waking up in the background due to iOS's delegate method didUpdateLocation getting called.

I am able to see print logs on the flutter side when didUpdateLocation gets called, but I am getting a missing plugin exception when I try to use path_provider.

I believe my issue is I am not registering the Applications plugins with the Flutter Engine that gets spawned in didUpdateLocations

Note: This location logic on the native iOS side is currently in the Main Flutter Application, not a plugin

Main question:

  • Where do I register a plugin for a dart isolate/headleess flutter engine in iOS native

Bonus questions:

  • do I need to manually destroy the flutter engine? (see cleanUpFlutterResources)
  • in regards to running while the app is terminated, is it best practice to create a flutter engine and destroy it every time a background iOS delegate callback is invoked, or should I just create one isolate with a background channel that is running all the time?
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {        
        if let location = locations.first {
            
            guard let callbackHandle = LocalStorage.getStoredCallbackHandle(),
                  let flutterCallbackInformation = FlutterCallbackCache.lookupCallbackInformation(callbackHandle) else {
                return
            }
            
            var flutterEngine: FlutterEngine? = FlutterEngine(name: "atc_location_swift.BGThread", project: nil, allowHeadlessExecution: true)
            
            flutterEngine!.run(withEntrypoint: flutterCallbackInformation.callbackName, libraryURI: flutterCallbackInformation.callbackLibraryPath)
            
            flutterEngine!.registrar(forPlugin: "FLTPathProviderPlugin")
            
            var backgroundMethodChannel: FlutterMethodChannel? = FlutterMethodChannel(name: "plugins.atc.io/location_background", binaryMessenger: flutterEngine!.binaryMessenger)
            
            func cleanupFlutterResources() {
                flutterEngine?.destroyContext()
                backgroundMethodChannel = nil
                flutterEngine = nil
            }
            
            backgroundMethodChannel?.setMethodCallHandler { (call, result) in
                    result(true)
                    backgroundMethodChannel?.invokeMethod("triggerBackgroundTask", arguments: ["lat": 0.1, "lon": 0.2], result: { flutterResult in
                        
                        print("flutter result \(flutterResult ?? "no result sent")")
                        
                        cleanupFlutterResources()
                    })
            }
            
            print("location: \(location.coordinate)")
        }
    }

Here is my dart isolate. Basically I am printing "callback called now -> $now" but when I try to addItemToDataList(), the path provider plugin gets referenced and throws a missing plugin exception

void callback() async {
  WidgetsFlutterBinding.ensureInitialized();

  const MethodChannel _backgroundChannel =
      MethodChannel('plugins.atc.io/location_background');

  _backgroundChannel.setMethodCallHandler((MethodCall call) async {
    try {
      WidgetsFlutterBinding.ensureInitialized();

      final String now = DateTime.now().toString();
      print("callback called now -> $now");
      await LocationDataSource.addItemToDataList(now);
      return Future.value(true);
    } catch (e) {
      print("exception calling callback: $e");
      return Future.value(false);
    }
  });
  _backgroundChannel.invokeMethod('backgroundChannelInitialized');
}

Here is my GeneratedPluginRegistrant, it seems like when calling flutter run, the dependency (path_provider) gets registered with the native code here, but that doesn't seem to do the trick when calling the path provider plugin from a dart isolate

#if __has_include(<path_provider/FLTPathProviderPlugin.h>)
#import <path_provider/FLTPathProviderPlugin.h>
#else
@import path_provider;
#endif

@implementation GeneratedPluginRegistrant

+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
  [FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]];
}

@end
0 Answers
Related