Repository navigation
Open
Labels
P3Issues that are less important to the Flutter projectIssues that are less important to the Flutter projectc: new featureNothing broken; request for a new capabilityNothing broken; request for a new capabilityengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.team-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team
Description
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
- Create a Plugin for iOS
- Inside the Plugin create a new FlutterEngine instance that allows headless execution
- Save a call handle
- Call the call handle using the new FlutterEngine
- 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.
Reacted by Stefan Schaller, Marcel Pfingstgräf, Julian Bissekkou, mro and Jérémie
Metadata
Metadata
Assignees
Labels
P3Issues that are less important to the Flutter projectIssues that are less important to the Flutter projectc: new featureNothing broken; request for a new capabilityNothing broken; request for a new capabilityengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.team-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team
Activity
stuartmorgan-g commentedon Jan 26, 2023
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?
JulianBissekkou commentedon Jan 26, 2023
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.
[-]iOS: Standalone FlutterEngine has no plugins registered[/-][+]Provide plugins access to generated plugin registrant[/+]stuartmorgan-g commentedon Jan 26, 2023
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.
JulianBissekkou commentedon Jan 26, 2023
Can you share an example of this code?
stuartmorgan-g commentedon Jan 30, 2023
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
FlutterPluginRegistryinstance, and have the plugin client call it with:in their app delegate. Then you can pass any engine instance you want wired up to that callback.