Skip to content

Provide plugins access to generated plugin registrant #119236

Open

Description

@JulianBissekkou

Use case:

I want to call a dart method from a FlutterEngine that allows headless execution. In this method I want to use flutter plugins and save data to shared preferences or show a local notification.

Edit: The problem is that we don't have access to GeneratedPluginRegistrant because it's generated in the App that uses the plugin.

Steps to Reproduce

  1. Create a Plugin for iOS
  2. Inside the Plugin create a new FlutterEngine instance that allows headless execution
  3. Save a call handle
  4. Call the call handle using the new FlutterEngine
  5. The dart method will be called, but no plugin can be used inside this method.
Code sample
import Flutter
import UIKit

public class MyPlugin: NSObject, FlutterPlugin {
    
    private var callbackHandler: Int64? = nil
    private let backgroundEngine = FlutterEngine.init(name: "background_dart_runner", project: nil, allowHeadlessExecution: true)
    
 
    public static func register(with registrar: FlutterPluginRegistrar) {
        let methodChannel = FlutterMethodChannel(name: "my_plugin", binaryMessenger: registrar.messenger())
        let instance = MyPlugin()
        
        registrar.addMethodCallDelegate(instance, channel: methodChannel)
    }
    
    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        // not important
    }
   
    
    private func onAddCallback(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
        guard let callbackHandler = call.arguments as? Int64 else {
            result(FlutterError(code: "invalid_parameter", message: "nil", details: nil))
            return
        }
        
        self.callbackHandler = callbackHandler
        result(nil)
    }
    
    private func onRemoveCallback(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
        self.callbackHandler = nil
        result(nil)
    }
    

    private func executeCurrentCallbackHandle() {
        guard let callbackHandler = callbackHandler else {
            return
        }
        guard let callbackInfo = FlutterCallbackCache.lookupCallbackInformation(callbackHandler) else {
            print("Callback can not be fetched.")
            return
        }
        let callbackName = callbackInfo.callbackName
        let callbackLibPath = callbackInfo.callbackLibraryPath
        
        let rootViewController = UIApplication.shared.keyWindow!.rootViewController as! FlutterViewController
        let currentEngine = rootViewController.engine!
        
        print("Background: Has app termination plugin: \(backgroundEngine.hasPlugin("AppTerminationCallbackPlugin"))")
        print("Background: Has app local notification plugin: \(backgroundEngine.hasPlugin("FlutterLocalNotificationsPlugin"))")
        print("Background: Has app shared prefs plugin: \(backgroundEngine.hasPlugin("SharedPreferencesPlugin"))")
        
        print("Engine: Has app termination plugin: \(currentEngine.hasPlugin("AppTerminationCallbackPlugin"))")
        print("Engine: Has app local notification plugin: \(currentEngine.hasPlugin("FlutterLocalNotificationsPlugin"))")
        print("Engine: Has app shared prefs plugin: \(currentEngine.hasPlugin("SharedPreferencesPlugin"))")
        
        let success = backgroundEngine.run(withEntrypoint: callbackName, libraryURI: callbackLibPath)
        print("Running engine success: \(success)")
    }
}
Logs
[✓] Flutter (Channel stable, 3.3.10, on macOS 13.0.1 22A400 darwin-arm (Rosetta), locale en-DE)
    • Flutter version 3.3.10 on channel stable at /Users/user/fvm/versions/3.3.10
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 135454af32 (6 weeks ago), 2022-12-15 07:36:55 -0800
    • Engine revision 3316dd8728
    • Dart version 2.18.6
    • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /Users/user/Library/Android/sdk
    • Platform android-33, build-tools 30.0.3
    • Java binary at: /Users/user/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14B47b
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.3)
    • Android Studio at /Users/user/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[!] Android Studio (version 2022.1)
    • Android Studio at /Users/user/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/221.6008.13.2211.9477386/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    ✗ Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[✓] IntelliJ IDEA Community Edition (version 2022.3)
    • IntelliJ at /Users/user/Library/Application Support/JetBrains/Toolbox/apps/IDEA-C/ch-0/223.7571.182/IntelliJ IDEA CE.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] IntelliJ IDEA Community Edition (version 2022.3.1)
    • IntelliJ at /Users/user/Library/Application Support/JetBrains/Toolbox/apps/IDEA-C/ch-0/223.8214.52/IntelliJ IDEA CE.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] VS Code (version 1.74.3)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.56.0

[✓] Connected device (3 available)
    • iPhone 14 Pro (mobile) • E9214A12-0EFA-41D0-8637-C7865E7C9998 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-1 (simulator)
    • macOS (desktop)        • macos                                • darwin-arm64   • macOS 13.0.1 22A400 darwin-arm (Rosetta)
    • Chrome (web)           • chrome                               • web-javascript • Google Chrome 109.0.5414.87

[✓] HTTP Host Availability
    • All required HTTP hosts are available

! Doctor found issues in 1 category.

Activity

  1. stuartmorgan-g commented on Jan 26, 2023

    @stuartmorgan-g
    Contributor

    The behavior described in the title is intentional, and it's not clear to me what exactly you want to be able to do since the use case is vague. Are you trying to make a plugin that creates a headless engine that has specific other plugins that you control registered, or that has every plugin from the application registered?

  2. JulianBissekkou commented on Jan 26, 2023

    @JulianBissekkou
    Author

    Yes, the engine should have every plugin from the application registered.

    Example use cases:
    Let's say I have a background process that is started when someone starts a VoIP call while my app is terminated or the OS wakes my app up to fetch some data. In that cases I don't have the "main" flutter engine running and I have to create the engine by myself.
    The flutter engine then calls the dart method as shown in the example code and in my dart method I want to save a timestamp in the shared_preferences.

    The Plugin that I am developing just offers the functionallity to register a callback handle and I will call it later on. When calling the callback I want the application to have the ability to use all the plugins.

  3. changed the title [-]iOS: Standalone FlutterEngine has no plugins registered[/-] [+]Provide plugins access to generated plugin registrant[/+] on Jan 26, 2023
  4. added
    c: new featureNothing broken; request for a new capability
    engineflutter/engine related. See also e: labels.
    on Jan 26, 2023
  5. stuartmorgan-g commented on Jan 26, 2023

    @stuartmorgan-g
    Contributor

    Retitling it for the specific request as I understand it then. We could consider building that into the registry.

    In the meantime, I believe developers of other plugins that want similar behavior have worked around it by providing a public API that allows clients of the plugin to add a line to their runner that wires up the generated registrant to the engine.

  6. JulianBissekkou commented on Jan 26, 2023

    @JulianBissekkou
    Author

    Can you share an example of this code?

  7. stuartmorgan-g commented on Jan 30, 2023

    @stuartmorgan-g
    Contributor

    I don't remember exactly where I've seen it done, sorry. But IIRC you do something like expose a global setter in your plugin for a callback that takes a FlutterPluginRegistry instance, and have the plugin client call it with:

    { pluginEngineRegistry in GeneratedPluginRegistrant.register(with: pluginEngineRegistry) }
    

    in their app delegate. Then you can pass any engine instance you want wired up to that callback.

  8. added
    P3Issues that are less important to the Flutter project
    on Jan 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: new featureNothing broken; request for a new capabilityengineflutter/engine related. See also e: labels.team-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions