5 #include <UIKit/UIKit.h>
6 #include "common/settings.h"
7 #define FML_USED_ON_EMBEDDER
13 #include "flutter/common/constants.h"
14 #include "flutter/fml/message_loop.h"
15 #include "flutter/fml/platform/darwin/platform_version.h"
16 #include "flutter/fml/trace_event.h"
17 #include "flutter/runtime/ptrace_check.h"
18 #include "flutter/shell/common/engine.h"
19 #include "flutter/shell/common/platform_view.h"
20 #include "flutter/shell/common/shell.h"
21 #include "flutter/shell/common/switches.h"
22 #include "flutter/shell/common/thread_host.h"
23 #include "flutter/shell/common/variable_refresh_rate_display.h"
24 #import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
27 #import "flutter/shell/platform/darwin/ios/InternalFlutterSwift/InternalFlutterSwift.h"
45 #include "flutter/shell/profiling/sampling_profiler.h"
53 fml::Thread::SetCurrentThreadName(config);
56 switch (config.priority) {
57 case fml::Thread::ThreadPriority::kBackground: {
58 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);
59 [[NSThread currentThread] setThreadPriority:0];
62 case fml::Thread::ThreadPriority::kNormal: {
63 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
64 [[NSThread currentThread] setThreadPriority:0.5];
67 case fml::Thread::ThreadPriority::kRaster:
68 case fml::Thread::ThreadPriority::kDisplay: {
69 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
70 [[NSThread currentThread] setThreadPriority:1.0];
73 pthread_t thread = pthread_self();
74 if (!pthread_getschedparam(thread, &policy, ¶m)) {
75 param.sched_priority = 50;
76 pthread_setschedparam(thread, policy, ¶m);
83 #pragma mark - Public exported constants
88 #pragma mark - Internal constants
97 @property(nonatomic, readonly) NSString*
key;
99 - (instancetype)initWithKey:(NSString*)key flutterEngine:(
FlutterEngine*)flutterEngine;
116 #pragma mark - Properties
119 @property(nonatomic, readonly, copy) NSString* labelPrefix;
120 @property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
121 @property(nonatomic, readonly, assign) BOOL restorationEnabled;
129 @property(nonatomic, readonly)
130 NSMutableDictionary<NSString*, FlutterEngineBaseRegistrar*>* registrars;
132 @property(nonatomic, readwrite, copy) NSString*
isolateId;
133 @property(nonatomic, copy) NSString* initialRoute;
134 @property(nonatomic, strong) id<NSObject> flutterViewControllerWillDeallocObserver;
136 @property(nonatomic, strong) FlutterConnectionCollection* connections;
137 @property(nonatomic, assign) int64_t nextTextureId;
139 #pragma mark - Channel properties
164 #pragma mark - Embedder API properties
181 _appRegistrar = [engine registrarForApplication:kFlutterApplicationRegistrarKey];
186 - (NSObject<FlutterPluginRegistry>*)pluginRegistry {
196 std::shared_ptr<flutter::ThreadHost> _threadHost;
206 - (int64_t)engineIdentifier {
207 return reinterpret_cast<int64_t
>((__bridge
void*)
self);
210 - (instancetype)init {
211 return [
self initWithName:@"FlutterEngine" project:nil allowHeadlessExecution:YES];
214 - (instancetype)initWithName:(NSString*)labelPrefix {
215 return [
self initWithName:labelPrefix project:nil allowHeadlessExecution:YES];
218 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
219 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
222 - (instancetype)initWithName:(NSString*)labelPrefix
224 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
225 return [
self initWithName:labelPrefix
227 allowHeadlessExecution:allowHeadlessExecution
228 restorationEnabled:NO];
231 - (instancetype)initWithName:(NSString*)labelPrefix
233 allowHeadlessExecution:(BOOL)allowHeadlessExecution
234 restorationEnabled:(BOOL)restorationEnabled {
236 NSAssert(
self,
@"Super init cannot be nil");
237 NSAssert(labelPrefix,
@"labelPrefix is required");
240 _allowHeadlessExecution = allowHeadlessExecution;
241 _labelPrefix = [labelPrefix copy];
244 _enableEmbedderAPI = _dartProject.
settings.enable_embedder_api;
245 if (_enableEmbedderAPI) {
246 NSLog(
@"============== iOS: enable_embedder_api is on ==============");
247 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
248 FlutterEngineGetProcAddresses(&_embedderAPI);
251 if (!EnableTracingIfNecessary(_dartProject.settings)) {
253 @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
254 @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
255 @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
256 @"profile and release mode apps can be launched from the home screen.");
260 _pluginPublications = [[NSMutableDictionary alloc] init];
261 _registrars = [[NSMutableDictionary alloc] init];
262 [
self recreatePlatformViewsController];
265 _connections = [[FlutterConnectionCollection alloc] init];
267 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
268 [center addObserver:self
269 selector:@selector(onMemoryWarning:)
270 name:UIApplicationDidReceiveMemoryWarningNotification
273 [
self setUpLifecycleNotifications:center];
275 [center addObserver:self
276 selector:@selector(onLocaleUpdated:)
277 name:NSCurrentLocaleDidChangeNotification
286 NSAssert([[NSThread currentThread] isMainThread],
@"Must be called on the main thread.");
287 return (__bridge
FlutterEngine*)
reinterpret_cast<void*
>(identifier);
290 - (void)setUpLifecycleNotifications:(NSNotificationCenter*)center {
292 [center addObserver:self
293 selector:@selector(sceneWillConnect:)
294 name:UISceneWillConnectNotification
297 [center addObserver:self
298 selector:@selector(sceneWillEnterForeground:)
299 name:UISceneWillEnterForegroundNotification
301 [center addObserver:self
302 selector:@selector(sceneDidEnterBackground:)
303 name:UISceneDidEnterBackgroundNotification
307 [center addObserver:self
308 selector:@selector(applicationWillEnterForeground:)
309 name:UIApplicationWillEnterForegroundNotification
311 [center addObserver:self
312 selector:@selector(applicationDidEnterBackground:)
313 name:UIApplicationDidEnterBackgroundNotification
317 - (void)sceneWillConnect:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
318 UIScene* scene = notification.object;
328 if (sceneLifeCycleDelegate != nil) {
329 return [sceneLifeCycleDelegate engine:self receivedConnectNotificationFor:scene];
334 - (void)recreatePlatformViewsController {
339 - (
flutter::IOSRenderingAPI)platformViewsRenderingAPI {
346 [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
347 if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
349 if ([registrar conformsToProtocol:@protocol(FlutterPluginRegistrar)]) {
350 [object detachFromEngineForRegistrar:((id<FlutterPluginRegistrar>)registrar)];
358 [_registrars enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineBaseRegistrar* registrar,
360 registrar.flutterEngine = nil;
366 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
367 if (_flutterViewControllerWillDeallocObserver) {
368 [center removeObserver:_flutterViewControllerWillDeallocObserver];
370 [center removeObserver:self];
378 - (void)updateViewportMetrics:(
flutter::ViewportMetrics)viewportMetrics {
379 if (!
self.platformView) {
382 self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
385 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet {
386 if (!
self.platformView) {
389 self.platformView->DispatchPointerDataPacket(std::move(packet));
392 - (void)installFirstFrameCallback:(
void (^)(
void))block {
393 if (!
self.platformView) {
398 self.
platformView->SetNextFrameCallback([weakSelf, block] {
403 FML_DCHECK(strongSelf.platformTaskRunner);
404 FML_DCHECK(strongSelf.rasterTaskRunner);
405 FML_DCHECK(strongSelf.rasterTaskRunner->RunsTasksOnCurrentThread());
407 strongSelf.platformTaskRunner->PostTask([block]() { block(); });
411 - (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags {
412 if (!
self.platformView) {
416 self.platformView->SetAccessibilityFeatures(flags);
419 - (void)notifyViewCreated {
420 if (!
self.platformView) {
423 self.platformView->NotifyCreated();
426 - (void)notifyViewDestroyed {
427 if (!
self.platformView) {
430 self.platformView->NotifyDestroyed();
433 - (
flutter::PlatformViewIOS*)platformView {
440 - (
fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
444 return _shell->GetTaskRunners().GetPlatformTaskRunner();
447 - (
fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
451 return _shell->GetTaskRunners().GetUITaskRunner();
454 - (
fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
458 return _shell->GetTaskRunners().GetRasterTaskRunner();
461 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
462 callback:(FlutterKeyEventCallback)callback
463 userData:(
void*)userData API_AVAILABLE(ios(13.4)) {
464 if (@available(iOS 13.4, *)) {
468 if (!
self.platformView) {
471 const char* character =
event.character;
473 flutter::KeyData key_data;
475 key_data.timestamp = (uint64_t)event.timestamp;
476 switch (event.type) {
477 case kFlutterKeyEventTypeUp:
478 key_data.type = flutter::KeyEventType::kUp;
480 case kFlutterKeyEventTypeDown:
481 key_data.type = flutter::KeyEventType::kDown;
483 case kFlutterKeyEventTypeRepeat:
484 key_data.type = flutter::KeyEventType::kRepeat;
487 key_data.physical =
event.physical;
488 key_data.logical =
event.logical;
489 key_data.synthesized =
event.synthesized;
491 auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
492 NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
494 auto response = ^(NSData* reply) {
495 if (callback ==
nullptr) {
498 BOOL handled = FALSE;
499 if (reply.length == 1 && *
reinterpret_cast<const uint8_t*
>(reply.bytes) == 1) {
502 callback(handled, userData);
505 [
self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
508 - (void)ensureSemanticsEnabled {
509 if (!
self.platformView) {
512 self.platformView->SetSemanticsEnabled(
true);
516 FML_DCHECK(
self.platformView);
518 self.platformView->SetOwnerViewController(_viewController);
519 [
self maybeSetupPlatformViewChannels];
520 [
self updateDisplays];
525 self.flutterViewControllerWillDeallocObserver =
526 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
527 object:viewController
528 queue:[NSOperationQueue mainQueue]
529 usingBlock:^(NSNotification* note) {
530 [weakSelf notifyViewControllerDeallocated];
533 self.flutterViewControllerWillDeallocObserver = nil;
534 [
self notifyLowMemory];
539 FML_DCHECK(
self.platformView);
540 self.platformView->attachView();
543 - (void)setFlutterViewControllerWillDeallocObserver:(
id<NSObject>)observer {
544 if (observer != _flutterViewControllerWillDeallocObserver) {
545 if (_flutterViewControllerWillDeallocObserver) {
546 [[NSNotificationCenter defaultCenter]
547 removeObserver:_flutterViewControllerWillDeallocObserver];
549 _flutterViewControllerWillDeallocObserver = observer;
553 - (void)notifyViewControllerDeallocated {
554 [
self.lifecycleChannel sendMessage:@"AppLifecycleState.detached"];
555 self.textInputPlugin.viewController = nil;
556 if (!
self.allowHeadlessExecution) {
557 [
self destroyContext];
558 }
else if (
self.platformView) {
559 self.platformView->SetOwnerViewController({});
561 [
self.textInputPlugin resetViewResponder];
562 _viewController = nil;
565 - (void)destroyContext {
566 [
self resetChannels];
567 self.isolateId = nil;
571 _platformViewsController = nil;
574 - (NSURL*)vmServiceUrl {
575 return self.publisher.url;
578 - (void)resetChannels {
579 self.localizationChannel = nil;
580 self.navigationChannel = nil;
581 self.restorationChannel = nil;
582 self.platformChannel = nil;
583 self.statusBarChannel = nil;
584 self.platformViewsChannel = nil;
585 self.textInputChannel = nil;
586 self.undoManagerChannel = nil;
587 self.scribbleChannel = nil;
588 self.lifecycleChannel = nil;
589 self.systemChannel = nil;
590 self.settingsChannel = nil;
591 self.keyEventChannel = nil;
592 self.spellCheckChannel = nil;
595 - (void)startProfiler {
596 FML_DCHECK(!_threadHost->name_prefix.empty());
597 _profiler = std::make_shared<flutter::SamplingProfiler>(
598 _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
600 flutter::ProfilerMetricsIOS profiler_metrics;
601 return profiler_metrics.GenerateSample();
610 - (void)setUpChannels {
614 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
615 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
624 binaryMessenger:self.binaryMessenger
627 self.navigationChannel =
629 binaryMessenger:self.binaryMessenger
632 if ([_initialRoute length] > 0) {
634 [
self.navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
638 self.restorationChannel =
640 binaryMessenger:self.binaryMessenger
643 self.platformChannel =
645 binaryMessenger:self.binaryMessenger
648 self.statusBarChannel =
650 binaryMessenger:self.binaryMessenger
652 [
self.statusBarChannel resizeChannelBuffer:0];
654 self.platformViewsChannel =
656 binaryMessenger:self.binaryMessenger
659 self.textInputChannel =
661 binaryMessenger:self.binaryMessenger
664 self.undoManagerChannel =
666 binaryMessenger:self.binaryMessenger
669 self.scribbleChannel =
671 binaryMessenger:self.binaryMessenger
674 self.spellCheckChannel =
676 binaryMessenger:self.binaryMessenger
679 self.lifecycleChannel =
681 binaryMessenger:self.binaryMessenger
686 binaryMessenger:self.binaryMessenger
689 self.settingsChannel =
691 binaryMessenger:self.binaryMessenger
694 self.keyEventChannel =
696 binaryMessenger:self.binaryMessenger
700 self.textInputPlugin.indirectScribbleDelegate =
self;
701 [
self.textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
706 self.restorationPlugin =
708 restorationEnabled:self.restorationEnabled];
711 self.screenshotChannel =
713 binaryMessenger:self.binaryMessenger
716 [
self.screenshotChannel setMethodCallHandler:^(FlutterMethodCall* _Nonnull call,
717 FlutterResult _Nonnull result) {
719 if (!(strongSelf && strongSelf->_shell && strongSelf->_shell->IsSetup())) {
722 message:@"Requesting screenshot while engine is not running."
725 flutter::Rasterizer::Screenshot screenshot =
726 [strongSelf screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData base64Encode:NO];
727 if (!screenshot.data) {
729 message:@"Unable to get screenshot."
733 NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
734 length:screenshot.data->size()];
735 NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
736 NSNumber* width = @(screenshot.frame_size.width);
737 NSNumber* height = @(screenshot.frame_size.height);
738 return result(@[ width, height, format ?: [NSNull null], data ]);
742 - (void)maybeSetupPlatformViewChannels {
743 if (
_shell &&
self.shell.IsSetup()) {
746 [
self.platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
747 [weakSelf.platformPlugin handleMethodCall:call result:result];
750 [
self.platformViewsChannel
751 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
753 [weakSelf.platformViewsController onMethodCall:call result:result];
757 [
self.textInputChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
758 [weakSelf.textInputPlugin handleMethodCall:call result:result];
761 [
self.undoManagerChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
762 [weakSelf.undoManagerPlugin handleMethodCall:call result:result];
765 [
self.spellCheckChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
766 [weakSelf.spellCheckPlugin handleMethodCall:call result:result];
771 - (
flutter::Rasterizer::Screenshot)screenshot:(
flutter::Rasterizer::ScreenshotType)type
772 base64Encode:(
bool)base64Encode {
773 return self.shell.Screenshot(type, base64Encode);
776 - (void)launchEngine:(NSString*)entrypoint
777 libraryURI:(NSString*)libraryOrNil
778 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
780 flutter::RunConfiguration configuration =
781 [
self.dartProject runConfigurationForEntrypoint:entrypoint
782 libraryOrNil:libraryOrNil
783 entrypointArgs:entrypointArgs];
785 configuration.SetEngineId(
self.engineIdentifier);
786 self.shell.RunEngine(std::move(configuration));
789 - (void)setUpShell:(std::unique_ptr<
flutter::Shell>)shell
790 withVMServicePublication:(BOOL)doesVMServicePublication {
791 _shell = std::move(shell);
792 [
self setUpChannels];
793 [
self onLocaleUpdated:nil];
794 [
self updateDisplays];
796 initWithEnableVMServicePublication:doesVMServicePublication];
797 [
self maybeSetupPlatformViewChannels];
798 _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
799 : flutter::GpuAvailability::kAvailable);
802 + (BOOL)isProfilerEnabled {
803 bool profilerEnabled =
false;
804 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
805 (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
806 profilerEnabled =
true;
808 return profilerEnabled;
811 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
812 static size_t s_shellCount = 0;
813 return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
816 static flutter::ThreadHost MakeThreadHost(NSString* thread_label,
817 const flutter::Settings& settings) {
820 fml::MessageLoop::EnsureInitializedForCurrentThread();
822 uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo;
823 if (settings.merged_platform_ui_thread != flutter::Settings::MergedPlatformUIThread::kEnabled) {
824 threadHostType |= flutter::ThreadHost::Type::kUi;
828 threadHostType = threadHostType | flutter::ThreadHost::Type::kProfiler;
831 flutter::ThreadHost::ThreadHostConfig host_config(thread_label.UTF8String, threadHostType,
834 host_config.ui_config =
835 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
836 flutter::ThreadHost::Type::kUi, thread_label.UTF8String),
837 fml::Thread::ThreadPriority::kDisplay);
838 host_config.raster_config =
839 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
840 flutter::ThreadHost::Type::kRaster, thread_label.UTF8String),
841 fml::Thread::ThreadPriority::kRaster);
843 host_config.io_config =
844 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
845 flutter::ThreadHost::Type::kIo, thread_label.UTF8String),
846 fml::Thread::ThreadPriority::kNormal);
848 return (flutter::ThreadHost){host_config};
851 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
853 FML_DCHECK(entrypoint) <<
"Must specify entrypoint if specifying library";
854 settings->advisory_script_entrypoint = entrypoint.UTF8String;
855 settings->advisory_script_uri = libraryURI.UTF8String;
856 }
else if (entrypoint) {
857 settings->advisory_script_entrypoint = entrypoint.UTF8String;
858 settings->advisory_script_uri = std::string(
"main.dart");
860 settings->advisory_script_entrypoint = std::string(
"main");
861 settings->advisory_script_uri = std::string(
"main.dart");
865 - (BOOL)createShell:(NSString*)entrypoint
866 libraryURI:(NSString*)libraryURI
867 initialRoute:(NSString*)initialRoute {
869 [FlutterLogger logWarning:@"This FlutterEngine was already invoked."];
873 self.initialRoute = initialRoute;
875 auto settings = [
self.dartProject settings];
876 if (initialRoute != nil) {
877 self.initialRoute = initialRoute;
878 }
else if (settings.route.empty() ==
false) {
879 self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
882 auto platformData = [
self.dartProject defaultPlatformData];
884 SetEntryPoint(&settings, entrypoint, libraryURI);
886 NSString* threadLabel = [
FlutterEngine generateThreadLabel:self.labelPrefix];
887 _threadHost = std::make_shared<flutter::ThreadHost>();
888 *_threadHost = MakeThreadHost(threadLabel, settings);
891 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
892 [weakSelf](flutter::Shell& shell) {
895 return std::unique_ptr<flutter::PlatformViewIOS>();
897 [strongSelf recreatePlatformViewsController];
898 strongSelf.platformViewsController.taskRunner =
899 shell.GetTaskRunners().GetPlatformTaskRunner();
900 return std::make_unique<flutter::PlatformViewIOS>(
901 shell, strongSelf->_renderingApi, strongSelf.platformViewsController,
902 shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
903 shell.GetIsGpuDisabledSyncSwitch());
906 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
907 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
909 fml::RefPtr<fml::TaskRunner> ui_runner;
910 if (settings.enable_impeller &&
911 settings.merged_platform_ui_thread == flutter::Settings::MergedPlatformUIThread::kEnabled) {
912 ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
914 ui_runner = _threadHost->ui_thread->GetTaskRunner();
916 flutter::TaskRunners task_runners(threadLabel.UTF8String,
917 fml::MessageLoop::GetCurrent().GetTaskRunner(),
918 _threadHost->raster_thread->GetTaskRunner(),
920 _threadHost->io_thread->GetTaskRunner()
924 self.isGpuDisabled =
self.viewController
925 ?
self.viewController.stateIsBackground
928 UIApplicationStateBackground;
931 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
935 on_create_platform_view,
936 on_create_rasterizer,
939 if (shell ==
nullptr) {
940 NSString* errorMessage = [NSString
941 stringWithFormat:@"Could not start a shell FlutterEngine with entrypoint: %@", entrypoint];
942 [FlutterLogger logError:errorMessage];
944 [
self setUpShell:std::move(shell)
945 withVMServicePublication:settings.enable_vm_service_publication];
947 [
self startProfiler];
954 - (BOOL)performImplicitEngineCallback {
957 id<FlutterImplicitEngineDelegate> provider = (id<FlutterImplicitEngineDelegate>)appDelegate;
959 initWithEngine:self]];
965 - (void)updateDisplays {
970 auto vsync_waiter =
_shell->GetVsyncWaiter().lock();
971 auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
972 std::vector<std::unique_ptr<flutter::Display>> displays;
973 auto screen_size = UIScreen.mainScreen.nativeBounds.size;
974 auto scale = UIScreen.mainScreen.scale;
975 displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
976 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
977 _shell->OnDisplayUpdates(std::move(displays));
981 return [
self runWithEntrypoint:FlutterDefaultDartEntrypoint
983 initialRoute:FlutterDefaultInitialRoute];
986 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
987 return [
self runWithEntrypoint:entrypoint
988 libraryURI:libraryURI
989 initialRoute:FlutterDefaultInitialRoute];
992 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
993 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
996 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
997 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
1000 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
1001 libraryURI:(NSString*)libraryURI
1002 initialRoute:(NSString*)initialRoute {
1003 return [
self runWithEntrypoint:entrypoint
1004 libraryURI:libraryURI
1005 initialRoute:initialRoute
1006 entrypointArgs:nil];
1009 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
1010 libraryURI:(NSString*)libraryURI
1011 initialRoute:(NSString*)initialRoute
1012 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
1013 if ([
self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
1014 [
self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
1017 return _shell !=
nullptr;
1020 - (void)notifyLowMemory {
1022 _shell->NotifyLowMemoryWarning();
1024 [
self.systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
1027 #pragma mark - Text input delegate
1030 updateEditingClient:(
int)client
1031 withState:(NSDictionary*)state {
1032 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingState"
1033 arguments:@[ @(client), state ]];
1037 updateEditingClient:(
int)client
1038 withState:(NSDictionary*)state
1039 withTag:(NSString*)tag {
1040 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithTag"
1041 arguments:@[ @(client), @{tag : state} ]];
1045 updateEditingClient:(
int)client
1046 withDelta:(NSDictionary*)delta {
1047 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
1048 arguments:@[ @(client), delta ]];
1052 updateFloatingCursor:(FlutterFloatingCursorDragState)state
1053 withClient:(
int)client
1054 withPosition:(NSDictionary*)position {
1055 NSString* stateString;
1057 case FlutterFloatingCursorDragStateStart:
1058 stateString =
@"FloatingCursorDragState.start";
1060 case FlutterFloatingCursorDragStateUpdate:
1061 stateString =
@"FloatingCursorDragState.update";
1063 case FlutterFloatingCursorDragStateEnd:
1064 stateString =
@"FloatingCursorDragState.end";
1067 [
self.textInputChannel invokeMethod:@"TextInputClient.updateFloatingCursor"
1068 arguments:@[ @(client), stateString, position ]];
1072 performAction:(FlutterTextInputAction)action
1073 withClient:(
int)client {
1074 NSString* actionString;
1076 case FlutterTextInputActionUnspecified:
1081 actionString =
@"TextInputAction.unspecified";
1083 case FlutterTextInputActionDone:
1084 actionString =
@"TextInputAction.done";
1086 case FlutterTextInputActionGo:
1087 actionString =
@"TextInputAction.go";
1089 case FlutterTextInputActionSend:
1090 actionString =
@"TextInputAction.send";
1092 case FlutterTextInputActionSearch:
1093 actionString =
@"TextInputAction.search";
1095 case FlutterTextInputActionNext:
1096 actionString =
@"TextInputAction.next";
1098 case FlutterTextInputActionContinue:
1099 actionString =
@"TextInputAction.continueAction";
1101 case FlutterTextInputActionJoin:
1102 actionString =
@"TextInputAction.join";
1104 case FlutterTextInputActionRoute:
1105 actionString =
@"TextInputAction.route";
1107 case FlutterTextInputActionEmergencyCall:
1108 actionString =
@"TextInputAction.emergencyCall";
1110 case FlutterTextInputActionNewline:
1111 actionString =
@"TextInputAction.newline";
1114 [
self.textInputChannel invokeMethod:@"TextInputClient.performAction"
1115 arguments:@[ @(client), actionString ]];
1119 showAutocorrectionPromptRectForStart:(NSUInteger)start
1121 withClient:(
int)client {
1122 [
self.textInputChannel invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1123 arguments:@[ @(client), @(start), @(end) ]];
1127 willDismissEditMenuWithTextInputClient:(
int)client {
1128 [
self.platformChannel invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
1129 arguments:@[ @(client) ]];
1133 shareSelectedText:(NSString*)selectedText {
1134 [
self.platformPlugin showShareViewController:selectedText];
1138 searchWebWithSelectedText:(NSString*)selectedText {
1139 [
self.platformPlugin searchWeb:selectedText];
1143 lookUpSelectedText:(NSString*)selectedText {
1144 [
self.platformPlugin showLookUpViewController:selectedText];
1148 performContextMenuCustomActionWithActionID:(NSString*)actionID
1149 textInputClient:(
int)client {
1150 [
self.platformChannel invokeMethod:@"ContextMenu.onPerformCustomAction"
1151 arguments:@[ @(client), actionID ]];
1154 #pragma mark - FlutterViewEngineDelegate
1160 [
self.textInputChannel invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1164 focusElement:(UIScribbleElementIdentifier)elementIdentifier
1165 atPoint:(CGPoint)referencePoint
1170 [
self.textInputChannel
1171 invokeMethod:@"TextInputClient.focusElement"
1172 arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1177 requestElementsInRect:(CGRect)rect
1182 [
self.textInputChannel
1183 invokeMethod:@"TextInputClient.requestElementsInRect"
1184 arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1192 [
self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1199 [
self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionFinished" arguments:nil];
1203 insertTextPlaceholderWithSize:(CGSize)size
1204 withClient:(
int)client {
1208 [
self.textInputChannel invokeMethod:@"TextInputClient.insertTextPlaceholder"
1209 arguments:@[ @(client), @(size.width), @(size.height) ]];
1213 removeTextPlaceholder:(
int)client {
1217 [
self.textInputChannel invokeMethod:@"TextInputClient.removeTextPlaceholder"
1218 arguments:@[ @(client) ]];
1222 didResignFirstResponderWithTextInputClient:(
int)client {
1226 [
self.textInputChannel invokeMethod:@"TextInputClient.onConnectionClosed"
1227 arguments:@[ @(client) ]];
1248 dispatch_async(dispatch_get_main_queue(), ^(
void) {
1249 long platform_view_id = [
self.platformViewsController firstResponderPlatformViewId];
1250 if (platform_view_id == -1) {
1254 [
self.platformViewsChannel invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1258 #pragma mark - Undo Manager Delegate
1260 - (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1261 NSString* action = (direction == FlutterUndoRedoDirectionUndo) ?
@"undo" :
@"redo";
1262 [
self.undoManagerChannel invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1265 - (UIView<UITextInput>*)activeTextInputView {
1266 return [[
self textInputPlugin] textInputView];
1269 - (NSUndoManager*)undoManager {
1270 return self.viewController.undoManager;
1273 #pragma mark - Screenshot Delegate
1275 - (
flutter::Rasterizer::Screenshot)takeScreenshot:(
flutter::Rasterizer::ScreenshotType)type
1276 asBase64Encoded:(BOOL)base64Encode {
1277 FML_DCHECK(
_shell) <<
"Cannot takeScreenshot without a shell";
1278 return _shell->Screenshot(type, base64Encode);
1281 - (void)flutterViewAccessibilityDidCall {
1283 [
self ensureSemanticsEnabled];
1305 #pragma mark - FlutterBinaryMessenger
1307 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1308 [
self sendOnChannel:channel message:message binaryReply:nil];
1311 - (void)sendOnChannel:(NSString*)channel
1312 message:(NSData*)message
1314 NSParameterAssert(channel);
1316 @"Sending a message before the FlutterEngine has been run.");
1317 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
1318 (callback == nil) ?
nullptr
1319 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1323 _shell->GetTaskRunners().GetPlatformTaskRunner());
1324 std::unique_ptr<flutter::PlatformMessage> platformMessage =
1325 (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1326 : std::make_unique<flutter::PlatformMessage>(
1329 _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1339 binaryMessageHandler:
1341 return [
self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1345 setMessageHandlerOnChannel:(NSString*)channel
1348 NSParameterAssert(channel);
1350 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1351 handler, taskQueue);
1352 return [
self.connections acquireConnectionForChannel:channel];
1354 NSAssert(!handler,
@"Setting a message handler before the FlutterEngine has been run.");
1356 return [FlutterConnectionCollection makeErrorConnectionWithErrorCode:-1L];
1362 NSString* channel = [
self.connections cleanupConnectionWithID:connection];
1363 if (channel.length > 0) {
1364 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String, nil,
1370 #pragma mark - FlutterTextureRegistry
1373 FML_DCHECK(
self.platformView);
1374 int64_t textureId =
self.nextTextureId++;
1375 self.platformView->RegisterExternalTexture(textureId, texture);
1379 - (void)unregisterTexture:(int64_t)textureId {
1380 _shell->GetPlatformView()->UnregisterTexture(textureId);
1383 - (void)textureFrameAvailable:(int64_t)textureId {
1384 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1387 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1391 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1395 - (id<FlutterPluginRegistry>)pluginRegistry {
1399 #pragma mark - FlutterPluginRegistry
1402 NSAssert(
self.pluginPublications[pluginKey] == nil,
@"Duplicate plugin key: %@", pluginKey);
1403 self.pluginPublications[pluginKey] = [NSNull null];
1405 flutterEngine:self];
1406 self.registrars[pluginKey] = result;
1411 NSAssert(
self.pluginPublications[key] == nil,
@"Duplicate key: %@", key);
1412 self.pluginPublications[key] = [NSNull null];
1415 self.registrars[key] = result;
1419 - (BOOL)hasPlugin:(NSString*)pluginKey {
1420 return _pluginPublications[pluginKey] != nil;
1423 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1424 return _pluginPublications[pluginKey];
1428 [
self.sceneLifeCycleDelegate addDelegate:delegate];
1431 #pragma mark - Notifications
1433 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1434 [
self flutterWillEnterForeground:notification];
1437 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1438 [
self flutterDidEnterBackground:notification];
1441 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1442 [
self flutterWillEnterForeground:notification];
1445 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1446 [
self flutterDidEnterBackground:notification];
1449 - (void)flutterWillEnterForeground:(NSNotification*)notification {
1450 [
self setIsGpuDisabled:NO];
1453 - (void)flutterDidEnterBackground:(NSNotification*)notification {
1454 [
self setIsGpuDisabled:YES];
1455 [
self notifyLowMemory];
1458 - (void)onMemoryWarning:(NSNotification*)notification {
1459 [
self notifyLowMemory];
1462 - (void)setIsGpuDisabled:(BOOL)value {
1464 _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1465 : flutter::GpuAvailability::kAvailable);
1467 _isGpuDisabled = value;
1470 #pragma mark - Locale updates
1472 - (void)onLocaleUpdated:(NSNotification*)notification {
1474 NSMutableArray<NSString*>* localeData = [[NSMutableArray alloc] init];
1475 NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1476 for (NSString* localeID in preferredLocales) {
1477 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1478 NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1479 NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1480 NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1481 NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1482 if (!languageCode) {
1485 [localeData addObject:languageCode];
1486 [localeData addObject:(countryCode ? countryCode : @"")];
1487 [localeData addObject:(scriptCode ? scriptCode : @"")];
1488 [localeData addObject:(variantCode ? variantCode : @"")];
1490 if (localeData.count == 0) {
1493 [
self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1496 - (void)onStatusBarTap {
1500 [
self.statusBarChannel invokeMethod:@"handleScrollToTop" arguments:nil];
1503 - (void)waitForFirstFrameSync:(NSTimeInterval)timeout
1504 callback:(NS_NOESCAPE
void (^_Nonnull)(BOOL didTimeout))callback {
1505 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1506 fml::Status status =
self.shell.WaitForFirstFrame(waitTime);
1507 callback(status.code() == fml::StatusCode::kDeadlineExceeded);
1510 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1511 callback:(
void (^_Nonnull)(BOOL didTimeout))callback {
1512 dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1513 dispatch_group_t group = dispatch_group_create();
1516 __block BOOL didTimeout = NO;
1517 dispatch_group_async(group, queue, ^{
1523 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1524 fml::Status status = strongSelf.
shell.WaitForFirstFrame(waitTime);
1525 didTimeout = status.code() == fml::StatusCode::kDeadlineExceeded;
1529 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
1543 callback(didTimeout);
1547 - (
FlutterEngine*)spawnWithEntrypoint:( NSString*)entrypoint
1548 libraryURI:( NSString*)libraryURI
1549 initialRoute:( NSString*)initialRoute
1550 entrypointArgs:( NSArray<NSString*>*)entrypointArgs {
1551 NSAssert(
_shell,
@"Spawning from an engine without a shell (possibly not run).");
1553 project:self.dartProject
1554 allowHeadlessExecution:self.allowHeadlessExecution];
1555 flutter::RunConfiguration configuration =
1556 [
self.dartProject runConfigurationForEntrypoint:entrypoint
1557 libraryOrNil:libraryURI
1558 entrypointArgs:entrypointArgs];
1560 configuration.SetEngineId(result.engineIdentifier);
1567 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->
GetIosContext();
1568 FML_DCHECK(context);
1572 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
1573 [result, context](flutter::Shell& shell) {
1574 [result recreatePlatformViewsController];
1575 result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
1576 return std::make_unique<flutter::PlatformViewIOS>(
1577 shell, context, result.platformViewsController, shell.GetTaskRunners());
1580 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
1581 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
1583 std::string cppInitialRoute;
1585 cppInitialRoute = [initialRoute UTF8String];
1588 std::unique_ptr<flutter::Shell> shell =
_shell->Spawn(
1589 std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1591 result->_threadHost = _threadHost;
1593 result->_isGpuDisabled = _isGpuDisabled;
1594 [result setUpShell:std::move(shell) withVMServicePublication:NO];
1598 - (const
flutter::ThreadHost&)threadHost {
1599 return *_threadHost;
1603 return self.dartProject;
1606 - (void)sendDeepLinkToFramework:(NSURL*)url completionHandler:(
void (^)(BOOL success))completion {
1608 [
self waitForFirstFrame:3.0
1609 callback:^(BOOL didTimeout) {
1612 logError:@"Timeout waiting for first frame when launching a URL."];
1616 [weakSelf.navigationChannel
1617 invokeMethod:@"pushRouteInformation"
1619 @"location" : url.absoluteString ?: [NSNull null],
1621 result:^(id _Nullable result) {
1623 [result isKindOfClass:[NSNumber class]] && [result boolValue];
1627 logError:@"Failed to handle route information in Flutter."];
1629 completion(success);
1639 - (instancetype)initWithKey:(NSString*)key flutterEngine:(
FlutterEngine*)flutterEngine {
1640 self = [
super init];
1641 NSAssert(
self,
@"Super init cannot be nil");
1648 return _flutterEngine.binaryMessenger;
1651 return _flutterEngine.textureRegistry;
1655 withId:(NSString*)factoryId {
1656 [
self registerViewFactory:factory
1658 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1662 withId:(NSString*)factoryId
1663 gestureRecognizersBlockingPolicy:
1665 [_flutterEngine.platformViewsController registerViewFactory:factory
1667 gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];
1675 return self.flutterEngine.viewController;
1678 - (void)publish:(NSObject*)value {
1679 self.flutterEngine.pluginPublications[
self.key] = value;
1682 - (void)addMethodCallDelegate:(NSObject<
FlutterPlugin>*)delegate
1689 - (void)addApplicationDelegate:(NSObject<
FlutterPlugin>*)delegate {
1692 id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1693 (id<FlutterAppLifeCycleProvider>)appDelegate;
1694 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1706 [
self.flutterEngine addSceneLifeCycleDelegate:delegate];
1709 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1710 return [
self.flutterEngine lookupKeyForAsset:asset];
1713 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1714 return [
self.flutterEngine lookupKeyForAsset:asset fromPackage:package];
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
int64_t FlutterBinaryMessengerConnection
void(^ FlutterResult)(id _Nullable result)
NSString *const FlutterDefaultDartEntrypoint
std::shared_ptr< flutter::SamplingProfiler > _profiler
std::unique_ptr< flutter::Shell > _shell
NSObject< FlutterApplicationRegistrar > * _appRegistrar
NSString *const kFlutterKeyDataChannel
NSString *const FlutterDefaultInitialRoute
flutter::IOSRenderingAPI _renderingApi
FlutterTextureRegistryRelay * _textureRegistry
static FLUTTER_ASSERT_ARC void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig &config)
static constexpr int kNumProfilerSamplesPerSec
NSString *const kFlutterApplicationRegistrarKey
FlutterBinaryMessengerRelay * _binaryMessenger
FlutterPlatformViewGestureRecognizersBlockingPolicy
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
FlutterRestorationPlugin * restorationPlugin
NSMutableDictionary * pluginPublications
FlutterEngineProcTable & embedderAPI
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
const flutter::Settings & settings()
NSString * lookupKeyForAsset:(NSString *asset)
NSObject< FlutterBinaryMessenger > * parent
FlutterEngine * flutterEngine
FlutterMethodChannel * textInputChannel
flutter::PlatformViewIOS * platformView()
FlutterMethodChannel * navigationChannel
FlutterBasicMessageChannel * keyEventChannel
FlutterBasicMessageChannel * lifecycleChannel
FlutterMethodChannel * platformChannel
FlutterMethodChannel * localizationChannel
FlutterBasicMessageChannel * systemChannel
FlutterBasicMessageChannel * settingsChannel
FlutterMethodChannel * restorationChannel
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
UIApplication * application
NSObject< FlutterTextureRegistry > * parent
fml::MallocMapping CopyNSDataToMapping(NSData *data)
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
NSObject< FlutterTextureRegistry > * textures()
NSObject< FlutterBinaryMessenger > * messenger()
instancetype sharedInstance()
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)