5 #include "common/settings.h"
6 #define FML_USED_ON_EMBEDDER
12 #include "flutter/common/constants.h"
13 #include "flutter/fml/message_loop.h"
14 #include "flutter/fml/platform/darwin/platform_version.h"
15 #include "flutter/fml/platform/darwin/weak_nsobject.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"
43 #include "flutter/shell/profiling/sampling_profiler.h"
49 fml::Thread::SetCurrentThreadName(config);
52 switch (config.priority) {
53 case fml::Thread::ThreadPriority::kBackground: {
54 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);
55 [[NSThread currentThread] setThreadPriority:0];
58 case fml::Thread::ThreadPriority::kNormal: {
59 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
60 [[NSThread currentThread] setThreadPriority:0.5];
63 case fml::Thread::ThreadPriority::kRaster:
64 case fml::Thread::ThreadPriority::kDisplay: {
65 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
66 [[NSThread currentThread] setThreadPriority:1.0];
69 pthread_t thread = pthread_self();
70 if (!pthread_getschedparam(thread, &policy, ¶m)) {
71 param.sched_priority = 50;
72 pthread_setschedparam(thread, policy, ¶m);
79 #pragma mark - Public exported constants
84 #pragma mark - Internal constants
102 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
103 @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
105 @property(nonatomic, readwrite, copy) NSString*
isolateId;
106 @property(nonatomic, copy) NSString* initialRoute;
107 @property(nonatomic, retain) id<NSObject> flutterViewControllerWillDeallocObserver;
109 #pragma mark - Embedder API properties
117 fml::scoped_nsobject<FlutterDartProject> _dartProject;
124 fml::scoped_nsobject<FlutterDartVMServicePublisher>
_publisher;
161 - (instancetype)init {
165 - (instancetype)initWithName:(NSString*)labelPrefix {
169 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
170 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
173 - (instancetype)initWithName:(NSString*)labelPrefix
175 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
176 return [
self initWithName:labelPrefix
178 allowHeadlessExecution:allowHeadlessExecution
179 restorationEnabled:NO];
182 - (instancetype)initWithName:(NSString*)labelPrefix
184 allowHeadlessExecution:(BOOL)allowHeadlessExecution
185 restorationEnabled:(BOOL)restorationEnabled {
187 NSAssert(
self,
@"Super init cannot be nil");
188 NSAssert(labelPrefix,
@"labelPrefix is required");
194 _weakFactory = std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(
self);
196 if (project == nil) {
199 _dartProject.reset([project retain]);
202 _enableEmbedderAPI = _dartProject.get().settings.enable_embedder_api;
203 if (_enableEmbedderAPI) {
204 NSLog(
@"============== iOS: enable_embedder_api is on ==============");
205 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
206 FlutterEngineGetProcAddresses(&_embedderAPI);
209 if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
211 @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
212 @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
213 @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
214 @"profile and release mode apps can be launched from the home screen.");
219 _pluginPublications = [[NSMutableDictionary alloc] init];
220 _registrars = [[NSMutableDictionary alloc] init];
221 [
self recreatePlatformViewController];
226 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
227 [center addObserver:self
228 selector:@selector(onMemoryWarning:)
229 name:UIApplicationDidReceiveMemoryWarningNotification
232 #if APPLICATION_EXTENSION_API_ONLY
233 if (@available(iOS 13.0, *)) {
234 [
self setUpSceneLifecycleNotifications:center];
236 [
self setUpApplicationLifecycleNotifications:center];
239 [
self setUpApplicationLifecycleNotifications:center];
242 [center addObserver:self
243 selector:@selector(onLocaleUpdated:)
244 name:NSCurrentLocaleDidChangeNotification
250 - (void)setUpSceneLifecycleNotifications:(NSNotificationCenter*)center API_AVAILABLE(ios(13.0)) {
251 [center addObserver:self
252 selector:@selector(sceneWillEnterForeground:)
253 name:UISceneWillEnterForegroundNotification
255 [center addObserver:self
256 selector:@selector(sceneDidEnterBackground:)
257 name:UISceneDidEnterBackgroundNotification
261 - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center {
262 [center addObserver:self
263 selector:@selector(applicationWillEnterForeground:)
264 name:UIApplicationWillEnterForegroundNotification
266 [center addObserver:self
267 selector:@selector(applicationDidEnterBackground:)
268 name:UIApplicationDidEnterBackgroundNotification
272 - (void)recreatePlatformViewController {
277 - (
flutter::IOSRenderingAPI)platformViewsRenderingAPI {
284 [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
285 if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
286 NSObject<FlutterPluginRegistrar>* registrar = self.registrars[key];
287 [object detachFromEngineForRegistrar:registrar];
291 [[NSNotificationCenter defaultCenter] postNotificationName:kFlutterEngineWillDealloc
301 enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
302 registrar.flutterEngine = nil;
305 [_labelPrefix release];
306 [_initialRoute release];
307 [_pluginPublications release];
308 [_registrars release];
311 [_binaryMessenger release];
312 [_textureRegistry release];
314 [_isolateId release];
316 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
317 if (_flutterViewControllerWillDeallocObserver) {
318 [center removeObserver:_flutterViewControllerWillDeallocObserver];
319 [_flutterViewControllerWillDeallocObserver release];
321 [center removeObserver:self];
331 - (fml::WeakNSObject<FlutterEngine>)getWeakNSObject {
335 - (void)updateViewportMetrics:(
flutter::ViewportMetrics)viewportMetrics {
336 if (!
self.platformView) {
339 self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
342 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet {
343 if (!
self.platformView) {
346 self.platformView->DispatchPointerDataPacket(std::move(packet));
349 - (fml::WeakPtr<flutter::PlatformView>)platformView {
351 return _shell->GetPlatformView();
354 - (
flutter::PlatformViewIOS*)iosPlatformView {
359 - (fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
361 return _shell->GetTaskRunners().GetPlatformTaskRunner();
364 - (fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
366 return _shell->GetTaskRunners().GetUITaskRunner();
369 - (fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
371 return _shell->GetTaskRunners().GetRasterTaskRunner();
374 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
375 callback:(FlutterKeyEventCallback)callback
376 userData:(
void*)userData API_AVAILABLE(ios(13.4)) {
377 if (@available(iOS 13.4, *)) {
381 if (!
self.platformView) {
384 const char* character =
event.character;
386 flutter::KeyData key_data;
388 key_data.timestamp = (uint64_t)event.timestamp;
389 switch (event.type) {
390 case kFlutterKeyEventTypeUp:
391 key_data.type = flutter::KeyEventType::kUp;
393 case kFlutterKeyEventTypeDown:
394 key_data.type = flutter::KeyEventType::kDown;
396 case kFlutterKeyEventTypeRepeat:
397 key_data.type = flutter::KeyEventType::kRepeat;
400 key_data.physical =
event.physical;
401 key_data.logical =
event.logical;
402 key_data.synthesized =
event.synthesized;
404 auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
405 NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
407 auto response = ^(NSData* reply) {
408 if (callback ==
nullptr) {
411 BOOL handled = FALSE;
412 if (reply.length == 1 && *
reinterpret_cast<const uint8_t*
>(reply.bytes) == 1) {
415 callback(handled, userData);
418 [
self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
421 - (void)ensureSemanticsEnabled {
422 self.iosPlatformView->SetSemanticsEnabled(
true);
426 FML_DCHECK(
self.iosPlatformView);
428 : fml::WeakNSObject<FlutterViewController>();
430 [
self maybeSetupPlatformViewChannels];
431 [
self updateDisplays];
436 self.flutterViewControllerWillDeallocObserver =
437 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
438 object:viewController
439 queue:[NSOperationQueue mainQueue]
440 usingBlock:^(NSNotification* note) {
441 [blockSelf notifyViewControllerDeallocated];
444 self.flutterViewControllerWillDeallocObserver = nil;
445 [
self notifyLowMemory];
450 self.iosPlatformView->attachView();
453 - (void)setFlutterViewControllerWillDeallocObserver:(
id<NSObject>)observer {
454 if (observer != _flutterViewControllerWillDeallocObserver) {
455 if (_flutterViewControllerWillDeallocObserver) {
456 [[NSNotificationCenter defaultCenter]
457 removeObserver:_flutterViewControllerWillDeallocObserver];
458 [_flutterViewControllerWillDeallocObserver release];
460 _flutterViewControllerWillDeallocObserver = [observer retain];
464 - (void)notifyViewControllerDeallocated {
465 [[
self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
468 [
self destroyContext];
475 [_textInputPlugin.get() resetViewResponder];
479 - (void)destroyContext {
480 [
self resetChannels];
481 self.isolateId = nil;
498 - (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
547 - (NSURL*)observatoryUrl {
548 return [_publisher.get() url];
551 - (NSURL*)vmServiceUrl {
552 return [_publisher.get() url];
555 - (void)resetChannels {
571 - (void)startProfiler {
574 _profiler = std::make_shared<flutter::SamplingProfiler>(
583 - (void)setUpChannels {
586 fml::WeakNSObject<FlutterEngine> weakSelf = [
self getWeakNSObject];
587 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
588 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
590 weakSelf.get().isolateId =
596 initWithName:
@"flutter/localization"
597 binaryMessenger:
self.binaryMessenger
601 initWithName:
@"flutter/navigation"
602 binaryMessenger:
self.binaryMessenger
605 if ([_initialRoute length] > 0) {
607 [_navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
608 [_initialRoute release];
613 initWithName:
@"flutter/restoration"
614 binaryMessenger:
self.binaryMessenger
618 initWithName:
@"flutter/platform"
619 binaryMessenger:
self.binaryMessenger
623 initWithName:
@"flutter/platform_views"
624 binaryMessenger:
self.binaryMessenger
628 initWithName:
@"flutter/textinput"
629 binaryMessenger:
self.binaryMessenger
633 initWithName:
@"flutter/undomanager"
634 binaryMessenger:
self.binaryMessenger
638 initWithName:
@"flutter/scribble"
639 binaryMessenger:
self.binaryMessenger
643 initWithName:
@"flutter/spellcheck"
644 binaryMessenger:
self.binaryMessenger
648 initWithName:
@"flutter/lifecycle"
649 binaryMessenger:
self.binaryMessenger
653 initWithName:
@"flutter/system"
654 binaryMessenger:
self.binaryMessenger
658 initWithName:
@"flutter/settings"
659 binaryMessenger:
self.binaryMessenger
663 initWithName:
@"flutter/keyevent"
664 binaryMessenger:
self.binaryMessenger
684 initWithName:
@"flutter/screenshot"
685 binaryMessenger:
self.binaryMessenger
688 [_screenshotChannel.get()
689 setMethodCallHandler:^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
690 if (!(weakSelf.get() && weakSelf.get()->_shell && weakSelf.get()->_shell->IsSetup())) {
693 message:@"Requesting screenshot while engine is not running."
696 flutter::Rasterizer::Screenshot screenshot =
697 [weakSelf.get() screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData
699 if (!screenshot.data) {
701 message:@"Unable to get screenshot."
705 NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
706 length:screenshot.data->size()];
707 NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
708 NSNumber* width = @(screenshot.frame_size.fWidth);
709 NSNumber* height = @(screenshot.frame_size.fHeight);
710 return result(@[ width, height, format ?: [NSNull null], data ]);
714 - (void)maybeSetupPlatformViewChannels {
715 if (
_shell &&
self.shell.IsSetup()) {
717 [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
721 fml::WeakNSObject<FlutterEngine> weakSelf = [
self getWeakNSObject];
722 [_platformViewsChannel.get()
723 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
725 weakSelf.get().platformViewsController->OnMethodCall(call, result);
730 [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
735 [_undoManagerChannel.get()
736 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
741 [_spellCheckChannel.get()
742 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
748 - (
flutter::Rasterizer::Screenshot)screenshot:(
flutter::Rasterizer::ScreenshotType)type
749 base64Encode:(
bool)base64Encode {
750 return self.shell.Screenshot(type, base64Encode);
753 - (void)launchEngine:(NSString*)entrypoint
754 libraryURI:(NSString*)libraryOrNil
755 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
757 self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
758 libraryOrNil:libraryOrNil
759 entrypointArgs:entrypointArgs]);
762 - (void)setUpShell:(std::unique_ptr<
flutter::Shell>)shell
763 withVMServicePublication:(BOOL)doesVMServicePublication {
764 _shell = std::move(shell);
765 [
self setUpChannels];
766 [
self onLocaleUpdated:nil];
767 [
self updateDisplays];
769 initWithEnableVMServicePublication:doesVMServicePublication]);
770 [
self maybeSetupPlatformViewChannels];
771 _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
772 : flutter::GpuAvailability::kAvailable);
775 + (BOOL)isProfilerEnabled {
776 bool profilerEnabled =
false;
777 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
778 (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
779 profilerEnabled =
true;
781 return profilerEnabled;
784 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
785 static size_t s_shellCount = 0;
786 return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
789 static flutter::ThreadHost MakeThreadHost(NSString* thread_label,
790 const flutter::Settings& settings) {
793 fml::MessageLoop::EnsureInitializedForCurrentThread();
795 uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo |
796 flutter::ThreadHost::Type::kUi;
799 threadHostType = threadHostType | flutter::ThreadHost::Type::kProfiler;
802 flutter::ThreadHost::ThreadHostConfig host_config(thread_label.UTF8String, threadHostType,
805 host_config.ui_config =
806 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
807 flutter::ThreadHost::Type::kUi, thread_label.UTF8String),
808 fml::Thread::ThreadPriority::kDisplay);
809 host_config.raster_config =
810 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
811 flutter::ThreadHost::Type::kRaster, thread_label.UTF8String),
812 fml::Thread::ThreadPriority::kRaster);
814 host_config.io_config =
815 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
816 flutter::ThreadHost::Type::kIo, thread_label.UTF8String),
817 fml::Thread::ThreadPriority::kNormal);
819 return (flutter::ThreadHost){host_config};
822 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
824 FML_DCHECK(entrypoint) <<
"Must specify entrypoint if specifying library";
825 settings->advisory_script_entrypoint = entrypoint.UTF8String;
826 settings->advisory_script_uri = libraryURI.UTF8String;
827 }
else if (entrypoint) {
828 settings->advisory_script_entrypoint = entrypoint.UTF8String;
829 settings->advisory_script_uri = std::string(
"main.dart");
831 settings->advisory_script_entrypoint = std::string(
"main");
832 settings->advisory_script_uri = std::string(
"main.dart");
836 - (BOOL)createShell:(NSString*)entrypoint
837 libraryURI:(NSString*)libraryURI
838 initialRoute:(NSString*)initialRoute {
840 FML_LOG(WARNING) <<
"This FlutterEngine was already invoked.";
844 self.initialRoute = initialRoute;
846 auto settings = [_dartProject.get() settings];
847 if (initialRoute != nil) {
848 self.initialRoute = initialRoute;
849 }
else if (settings.route.empty() ==
false) {
850 self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
855 auto platformData = [_dartProject.get() defaultPlatformData];
857 SetEntryPoint(&settings, entrypoint, libraryURI);
859 NSString* threadLabel = [
FlutterEngine generateThreadLabel:_labelPrefix];
860 _threadHost = std::make_shared<flutter::ThreadHost>();
861 *
_threadHost = MakeThreadHost(threadLabel, settings);
865 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
866 [
self](flutter::Shell& shell) {
867 [
self recreatePlatformViewController];
868 self->_platformViewsController->SetTaskRunner(
869 shell.GetTaskRunners().GetPlatformTaskRunner());
870 return std::make_unique<flutter::PlatformViewIOS>(
871 shell,
self->_renderingApi,
self->_platformViewsController, shell.GetTaskRunners(),
872 shell.GetConcurrentWorkerTaskRunner(), shell.GetIsGpuDisabledSyncSwitch());
875 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
876 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
878 fml::RefPtr<fml::TaskRunner> ui_runner =
_threadHost->ui_thread->GetTaskRunner();
879 flutter::TaskRunners task_runners(threadLabel.UTF8String,
880 fml::MessageLoop::GetCurrent().GetTaskRunner(),
886 #if APPLICATION_EXTENSION_API_ONLY
887 if (@available(iOS 13.0, *)) {
888 _isGpuDisabled =
self.viewController.flutterWindowSceneIfViewLoaded.activationState ==
889 UISceneActivationStateBackground;
897 [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
901 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
905 on_create_platform_view,
906 on_create_rasterizer,
909 if (shell ==
nullptr) {
910 FML_LOG(ERROR) <<
"Could not start a shell FlutterEngine with entrypoint: "
911 << entrypoint.UTF8String;
914 FML_LOG(INFO) <<
"Enabled VM Service Publication: " << settings.enable_vm_service_publication;
915 [
self setUpShell:std::move(shell)
916 withVMServicePublication:settings.enable_vm_service_publication];
918 [
self startProfiler];
925 - (void)updateDisplays {
930 auto vsync_waiter =
_shell->GetVsyncWaiter().lock();
931 auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
932 std::vector<std::unique_ptr<flutter::Display>> displays;
933 auto screen_size = UIScreen.mainScreen.nativeBounds.size;
934 auto scale = UIScreen.mainScreen.scale;
935 displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
936 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
937 _shell->OnDisplayUpdates(std::move(displays));
946 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
947 return [
self runWithEntrypoint:entrypoint
948 libraryURI:libraryURI
949 initialRoute:FlutterDefaultInitialRoute];
952 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
953 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
956 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
957 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
960 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
961 libraryURI:(NSString*)libraryURI
962 initialRoute:(NSString*)initialRoute {
963 return [
self runWithEntrypoint:entrypoint
964 libraryURI:libraryURI
965 initialRoute:initialRoute
969 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
970 libraryURI:(NSString*)libraryURI
971 initialRoute:(NSString*)initialRoute
972 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
973 if ([
self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
974 [
self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
980 - (void)notifyLowMemory {
982 _shell->NotifyLowMemoryWarning();
984 [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
987 #pragma mark - Text input delegate
990 updateEditingClient:(
int)client
991 withState:(NSDictionary*)state {
992 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState"
993 arguments:@[ @(client), state ]];
997 updateEditingClient:(
int)client
998 withState:(NSDictionary*)state
999 withTag:(NSString*)tag {
1000 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag"
1001 arguments:@[ @(client), @{tag : state} ]];
1005 updateEditingClient:(
int)client
1006 withDelta:(NSDictionary*)delta {
1007 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
1008 arguments:@[ @(client), delta ]];
1012 updateFloatingCursor:(FlutterFloatingCursorDragState)state
1013 withClient:(
int)client
1014 withPosition:(NSDictionary*)position {
1015 NSString* stateString;
1017 case FlutterFloatingCursorDragStateStart:
1018 stateString =
@"FloatingCursorDragState.start";
1020 case FlutterFloatingCursorDragStateUpdate:
1021 stateString =
@"FloatingCursorDragState.update";
1023 case FlutterFloatingCursorDragStateEnd:
1024 stateString =
@"FloatingCursorDragState.end";
1027 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor"
1028 arguments:@[ @(client), stateString, position ]];
1032 performAction:(FlutterTextInputAction)action
1033 withClient:(
int)client {
1034 NSString* actionString;
1036 case FlutterTextInputActionUnspecified:
1041 actionString =
@"TextInputAction.unspecified";
1043 case FlutterTextInputActionDone:
1044 actionString =
@"TextInputAction.done";
1046 case FlutterTextInputActionGo:
1047 actionString =
@"TextInputAction.go";
1049 case FlutterTextInputActionSend:
1050 actionString =
@"TextInputAction.send";
1052 case FlutterTextInputActionSearch:
1053 actionString =
@"TextInputAction.search";
1055 case FlutterTextInputActionNext:
1056 actionString =
@"TextInputAction.next";
1058 case FlutterTextInputActionContinue:
1059 actionString =
@"TextInputAction.continueAction";
1061 case FlutterTextInputActionJoin:
1062 actionString =
@"TextInputAction.join";
1064 case FlutterTextInputActionRoute:
1065 actionString =
@"TextInputAction.route";
1067 case FlutterTextInputActionEmergencyCall:
1068 actionString =
@"TextInputAction.emergencyCall";
1070 case FlutterTextInputActionNewline:
1071 actionString =
@"TextInputAction.newline";
1074 [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction"
1075 arguments:@[ @(client), actionString ]];
1079 showAutocorrectionPromptRectForStart:(NSUInteger)start
1081 withClient:(
int)client {
1082 [_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1083 arguments:@[ @(client), @(start), @(end) ]];
1087 willDismissEditMenuWithTextInputClient:(
int)client {
1088 [_platformChannel.get() invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
1089 arguments:@[ @(client) ]];
1092 #pragma mark - FlutterViewEngineDelegate
1098 [_textInputChannel.get() invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1102 focusElement:(UIScribbleElementIdentifier)elementIdentifier
1103 atPoint:(CGPoint)referencePoint
1108 [_textInputChannel.get()
1109 invokeMethod:@"TextInputClient.focusElement"
1110 arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1115 requestElementsInRect:(CGRect)rect
1120 [_textInputChannel.get()
1121 invokeMethod:@"TextInputClient.requestElementsInRect"
1122 arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1130 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1137 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionFinished"
1142 insertTextPlaceholderWithSize:(CGSize)size
1143 withClient:(
int)client {
1147 [_textInputChannel.get() invokeMethod:@"TextInputClient.insertTextPlaceholder"
1148 arguments:@[ @(client), @(size.width), @(size.height) ]];
1152 removeTextPlaceholder:(
int)client {
1156 [_textInputChannel.get() invokeMethod:@"TextInputClient.removeTextPlaceholder"
1157 arguments:@[ @(client) ]];
1161 didResignFirstResponderWithTextInputClient:(
int)client {
1165 [_textInputChannel.get() invokeMethod:@"TextInputClient.onConnectionClosed"
1166 arguments:@[ @(client) ]];
1187 dispatch_async(dispatch_get_main_queue(), ^(
void) {
1188 long platform_view_id =
self.platformViewsController->FindFirstResponderPlatformViewId();
1189 if (platform_view_id == -1) {
1193 [_platformViewsChannel.get() invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1197 #pragma mark - Undo Manager Delegate
1199 - (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1200 NSString* action = (direction == FlutterUndoRedoDirectionUndo) ?
@"undo" :
@"redo";
1201 [_undoManagerChannel.get() invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1204 - (UIView<UITextInput>*)activeTextInputView {
1205 return [[
self textInputPlugin] textInputView];
1208 - (NSUndoManager*)undoManager {
1209 return self.viewController.undoManager;
1212 #pragma mark - Screenshot Delegate
1214 - (
flutter::Rasterizer::Screenshot)takeScreenshot:(
flutter::Rasterizer::ScreenshotType)type
1215 asBase64Encoded:(BOOL)base64Encode {
1216 FML_DCHECK(
_shell) <<
"Cannot takeScreenshot without a shell";
1217 return _shell->Screenshot(type, base64Encode);
1220 - (void)flutterViewAccessibilityDidCall {
1222 [
self ensureSemanticsEnabled];
1240 [_binaryMessenger release];
1245 #pragma mark - FlutterBinaryMessenger
1247 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1248 [
self sendOnChannel:channel message:message binaryReply:nil];
1251 - (void)sendOnChannel:(NSString*)channel
1252 message:(NSData*)message
1254 NSParameterAssert(channel);
1256 @"Sending a message before the FlutterEngine has been run.");
1257 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
1258 (callback == nil) ?
nullptr
1259 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1263 _shell->GetTaskRunners().GetPlatformTaskRunner());
1264 std::unique_ptr<flutter::PlatformMessage> platformMessage =
1265 (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1266 : std::make_unique<flutter::PlatformMessage>(
1269 _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1279 binaryMessageHandler:
1281 return [
self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1285 setMessageHandlerOnChannel:(NSString*)channel
1288 NSParameterAssert(channel);
1290 self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1291 handler, taskQueue);
1292 return _connections->AquireConnection(channel.UTF8String);
1294 NSAssert(!handler,
@"Setting a message handler before the FlutterEngine has been run.");
1302 std::string channel =
_connections->CleanupConnection(connection);
1303 if (!channel.empty()) {
1304 self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
1310 #pragma mark - FlutterTextureRegistry
1314 self.iosPlatformView->RegisterExternalTexture(textureId, texture);
1318 - (void)unregisterTexture:(int64_t)textureId {
1319 _shell->GetPlatformView()->UnregisterTexture(textureId);
1322 - (void)textureFrameAvailable:(int64_t)textureId {
1323 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1326 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1330 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1334 - (id<FlutterPluginRegistry>)pluginRegistry {
1338 #pragma mark - FlutterPluginRegistry
1341 NSAssert(
self.pluginPublications[pluginKey] == nil,
@"Duplicate plugin key: %@", pluginKey);
1342 self.pluginPublications[pluginKey] = [NSNull null];
1344 flutterEngine:self];
1345 self.registrars[pluginKey] = result;
1346 return [result autorelease];
1349 - (BOOL)hasPlugin:(NSString*)pluginKey {
1350 return _pluginPublications[pluginKey] != nil;
1353 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1354 return _pluginPublications[pluginKey];
1357 #pragma mark - Notifications
1359 #if APPLICATION_EXTENSION_API_ONLY
1360 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1361 [
self flutterWillEnterForeground:notification];
1364 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1365 [
self flutterDidEnterBackground:notification];
1368 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1369 [
self flutterWillEnterForeground:notification];
1372 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1373 [
self flutterDidEnterBackground:notification];
1377 - (void)flutterWillEnterForeground:(NSNotification*)notification {
1378 [
self setIsGpuDisabled:NO];
1381 - (void)flutterDidEnterBackground:(NSNotification*)notification {
1382 [
self setIsGpuDisabled:YES];
1383 [
self notifyLowMemory];
1386 - (void)onMemoryWarning:(NSNotification*)notification {
1387 [
self notifyLowMemory];
1390 - (void)setIsGpuDisabled:(BOOL)value {
1392 _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1393 : flutter::GpuAvailability::kAvailable);
1395 _isGpuDisabled = value;
1398 #pragma mark - Locale updates
1400 - (void)onLocaleUpdated:(NSNotification*)notification {
1402 NSMutableArray<NSString*>* localeData = [[[NSMutableArray alloc] init] autorelease];
1403 NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1404 for (NSString* localeID in preferredLocales) {
1405 NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
1406 NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1407 NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1408 NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1409 NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1410 if (!languageCode) {
1413 [localeData addObject:languageCode];
1414 [localeData addObject:(countryCode ? countryCode : @"")];
1415 [localeData addObject:(scriptCode ? scriptCode : @"")];
1416 [localeData addObject:(variantCode ? variantCode : @"")];
1418 if (localeData.count == 0) {
1421 [
self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1424 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1425 callback:(
void (^_Nonnull)(BOOL didTimeout))callback {
1426 dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1427 dispatch_async(queue, ^{
1428 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1430 self.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
1431 dispatch_async(dispatch_get_main_queue(), ^{
1432 callback(didTimeout);
1437 - (
FlutterEngine*)spawnWithEntrypoint:( NSString*)entrypoint
1438 libraryURI:( NSString*)libraryURI
1439 initialRoute:( NSString*)initialRoute
1440 entrypointArgs:( NSArray<NSString*>*)entrypointArgs {
1441 NSAssert(
_shell,
@"Spawning from an engine without a shell (possibly not run).");
1443 project:_dartProject.get()
1444 allowHeadlessExecution:_allowHeadlessExecution];
1445 flutter::RunConfiguration configuration =
1446 [_dartProject.get() runConfigurationForEntrypoint:entrypoint
1447 libraryOrNil:libraryURI
1448 entrypointArgs:entrypointArgs];
1455 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->
GetIosContext();
1456 FML_DCHECK(context);
1460 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
1461 [result, context](flutter::Shell& shell) {
1462 [result recreatePlatformViewController];
1463 result->_platformViewsController->SetTaskRunner(
1464 shell.GetTaskRunners().GetPlatformTaskRunner());
1465 return std::make_unique<flutter::PlatformViewIOS>(
1466 shell, context, result->_platformViewsController, shell.GetTaskRunners());
1469 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
1470 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
1472 std::string cppInitialRoute;
1474 cppInitialRoute = [initialRoute UTF8String];
1477 std::unique_ptr<flutter::Shell> shell =
_shell->Spawn(
1478 std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1483 result->_isGpuDisabled = _isGpuDisabled;
1484 [result setUpShell:std::move(shell) withVMServicePublication:NO];
1485 return [result autorelease];
1488 - (const
flutter::ThreadHost&)threadHost {
1493 return _dartProject.get();
1496 - (BOOL)isUsingImpeller {
1503 NSString* _pluginKey;
1506 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
1507 self = [
super init];
1508 NSAssert(
self,
@"Super init cannot be nil");
1509 _pluginKey = [pluginKey copy];
1510 _flutterEngine = flutterEngine;
1515 [_pluginKey release];
1520 return _flutterEngine.binaryMessenger;
1524 return _flutterEngine.textureRegistry;
1527 - (void)publish:(NSObject*)value {
1528 _flutterEngine.pluginPublications[_pluginKey] = value;
1531 - (void)addMethodCallDelegate:(NSObject<
FlutterPlugin>*)delegate
1538 - (void)addApplicationDelegate:(NSObject<
FlutterPlugin>*)delegate
1539 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions") {
1540 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
1542 id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1543 (id<FlutterAppLifeCycleProvider>)appDelegate;
1544 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1548 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1549 return [_flutterEngine lookupKeyForAsset:asset];
1552 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1553 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
1557 withId:(NSString*)factoryId {
1558 [
self registerViewFactory:factory
1560 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1564 withId:(NSString*)factoryId
1565 gestureRecognizersBlockingPolicy:
1567 [_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
1568 gestureRecognizersBlockingPolicy);