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/trace_event.h"
16 #include "flutter/runtime/ptrace_check.h"
17 #include "flutter/shell/common/engine.h"
18 #include "flutter/shell/common/platform_view.h"
19 #include "flutter/shell/common/shell.h"
20 #include "flutter/shell/common/switches.h"
21 #include "flutter/shell/common/thread_host.h"
22 #include "flutter/shell/common/variable_refresh_rate_display.h"
42 #include "flutter/shell/profiling/sampling_profiler.h"
50 fml::Thread::SetCurrentThreadName(config);
53 switch (config.priority) {
54 case fml::Thread::ThreadPriority::kBackground: {
55 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);
56 [[NSThread currentThread] setThreadPriority:0];
59 case fml::Thread::ThreadPriority::kNormal: {
60 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
61 [[NSThread currentThread] setThreadPriority:0.5];
64 case fml::Thread::ThreadPriority::kRaster:
65 case fml::Thread::ThreadPriority::kDisplay: {
66 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
67 [[NSThread currentThread] setThreadPriority:1.0];
70 pthread_t thread = pthread_self();
71 if (!pthread_getschedparam(thread, &policy, ¶m)) {
72 param.sched_priority = 50;
73 pthread_setschedparam(thread, policy, ¶m);
80 #pragma mark - Public exported constants
85 #pragma mark - Internal constants
101 #pragma mark - Properties
104 @property(nonatomic, readonly, copy) NSString* labelPrefix;
105 @property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
106 @property(nonatomic, readonly, assign) BOOL restorationEnabled;
112 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
113 @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
115 @property(nonatomic, readwrite, copy) NSString*
isolateId;
116 @property(nonatomic, copy) NSString* initialRoute;
117 @property(nonatomic, strong) id<NSObject> flutterViewControllerWillDeallocObserver;
119 @property(nonatomic, assign) int64_t nextTextureId;
121 #pragma mark - Channel properties
143 #pragma mark - Embedder API properties
152 std::shared_ptr<flutter::ThreadHost> _threadHost;
153 std::unique_ptr<flutter::Shell>
_shell;
163 - (instancetype)init {
167 - (instancetype)initWithName:(NSString*)labelPrefix {
171 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
172 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
175 - (instancetype)initWithName:(NSString*)labelPrefix
177 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
178 return [
self initWithName:labelPrefix
180 allowHeadlessExecution:allowHeadlessExecution
181 restorationEnabled:NO];
184 - (instancetype)initWithName:(NSString*)labelPrefix
186 allowHeadlessExecution:(BOOL)allowHeadlessExecution
187 restorationEnabled:(BOOL)restorationEnabled {
189 NSAssert(
self,
@"Super init cannot be nil");
190 NSAssert(labelPrefix,
@"labelPrefix is required");
193 _allowHeadlessExecution = allowHeadlessExecution;
194 _labelPrefix = [labelPrefix copy];
197 _enableEmbedderAPI = _dartProject.
settings.enable_embedder_api;
198 if (_enableEmbedderAPI) {
199 NSLog(
@"============== iOS: enable_embedder_api is on ==============");
200 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
201 FlutterEngineGetProcAddresses(&_embedderAPI);
204 if (!EnableTracingIfNecessary(_dartProject.settings)) {
206 @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
207 @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
208 @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
209 @"profile and release mode apps can be launched from the home screen.");
213 _pluginPublications = [[NSMutableDictionary alloc] init];
214 _registrars = [[NSMutableDictionary alloc] init];
215 [
self recreatePlatformViewsController];
220 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
221 [center addObserver:self
222 selector:@selector(onMemoryWarning:)
223 name:UIApplicationDidReceiveMemoryWarningNotification
226 #if APPLICATION_EXTENSION_API_ONLY
227 if (@available(iOS 13.0, *)) {
228 [
self setUpSceneLifecycleNotifications:center];
230 [
self setUpApplicationLifecycleNotifications:center];
233 [
self setUpApplicationLifecycleNotifications:center];
236 [center addObserver:self
237 selector:@selector(onLocaleUpdated:)
238 name:NSCurrentLocaleDidChangeNotification
244 - (void)setUpSceneLifecycleNotifications:(NSNotificationCenter*)center API_AVAILABLE(ios(13.0)) {
245 [center addObserver:self
246 selector:@selector(sceneWillEnterForeground:)
247 name:UISceneWillEnterForegroundNotification
249 [center addObserver:self
250 selector:@selector(sceneDidEnterBackground:)
251 name:UISceneDidEnterBackgroundNotification
255 - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center {
256 [center addObserver:self
257 selector:@selector(applicationWillEnterForeground:)
258 name:UIApplicationWillEnterForegroundNotification
260 [center addObserver:self
261 selector:@selector(applicationDidEnterBackground:)
262 name:UIApplicationDidEnterBackgroundNotification
266 - (void)recreatePlatformViewsController {
271 - (
flutter::IOSRenderingAPI)platformViewsRenderingAPI {
278 [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
279 if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
280 NSObject<FlutterPluginRegistrar>* registrar = self.registrars[key];
281 [object detachFromEngineForRegistrar:registrar];
289 enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
290 registrar.flutterEngine = nil;
296 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
297 if (_flutterViewControllerWillDeallocObserver) {
298 [center removeObserver:_flutterViewControllerWillDeallocObserver];
300 [center removeObserver:self];
308 - (void)updateViewportMetrics:(
flutter::ViewportMetrics)viewportMetrics {
309 if (!
self.platformView) {
312 self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
315 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet {
316 if (!
self.platformView) {
319 self.platformView->DispatchPointerDataPacket(std::move(packet));
322 - (void)installFirstFrameCallback:(
void (^)(
void))block {
323 if (!
self.platformView) {
328 self.
platformView->SetNextFrameCallback([weakSelf, block] {
333 FML_DCHECK(strongSelf.platformTaskRunner);
334 FML_DCHECK(strongSelf.rasterTaskRunner);
335 FML_DCHECK(strongSelf.rasterTaskRunner->RunsTasksOnCurrentThread());
337 strongSelf.platformTaskRunner->PostTask([block]() { block(); });
341 - (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags {
342 if (!
self.platformView) {
346 self.platformView->SetAccessibilityFeatures(flags);
349 - (void)notifyViewCreated {
350 if (!
self.platformView) {
353 self.platformView->NotifyCreated();
356 - (void)notifyViewDestroyed {
357 if (!
self.platformView) {
360 self.platformView->NotifyDestroyed();
363 - (
flutter::PlatformViewIOS*)platformView {
370 - (
fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
374 return _shell->GetTaskRunners().GetPlatformTaskRunner();
377 - (
fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
381 return _shell->GetTaskRunners().GetUITaskRunner();
384 - (
fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
388 return _shell->GetTaskRunners().GetRasterTaskRunner();
391 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
392 callback:(FlutterKeyEventCallback)callback
393 userData:(
void*)userData API_AVAILABLE(ios(13.4)) {
394 if (@available(iOS 13.4, *)) {
398 if (!
self.platformView) {
401 const char* character =
event.character;
403 flutter::KeyData key_data;
405 key_data.timestamp = (uint64_t)event.timestamp;
406 switch (event.type) {
407 case kFlutterKeyEventTypeUp:
408 key_data.type = flutter::KeyEventType::kUp;
410 case kFlutterKeyEventTypeDown:
411 key_data.type = flutter::KeyEventType::kDown;
413 case kFlutterKeyEventTypeRepeat:
414 key_data.type = flutter::KeyEventType::kRepeat;
417 key_data.physical =
event.physical;
418 key_data.logical =
event.logical;
419 key_data.synthesized =
event.synthesized;
421 auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
422 NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
424 auto response = ^(NSData* reply) {
425 if (callback ==
nullptr) {
428 BOOL handled = FALSE;
429 if (reply.length == 1 && *
reinterpret_cast<const uint8_t*
>(reply.bytes) == 1) {
432 callback(handled, userData);
435 [
self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
438 - (void)ensureSemanticsEnabled {
439 if (!
self.platformView) {
442 self.platformView->SetSemanticsEnabled(
true);
446 FML_DCHECK(
self.platformView);
448 self.platformView->SetOwnerViewController(_viewController);
449 [
self maybeSetupPlatformViewChannels];
450 [
self updateDisplays];
455 self.flutterViewControllerWillDeallocObserver =
456 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
457 object:viewController
458 queue:[NSOperationQueue mainQueue]
459 usingBlock:^(NSNotification* note) {
460 [weakSelf notifyViewControllerDeallocated];
463 self.flutterViewControllerWillDeallocObserver = nil;
464 [
self notifyLowMemory];
469 FML_DCHECK(
self.platformView);
470 self.platformView->attachView();
473 - (void)setFlutterViewControllerWillDeallocObserver:(
id<NSObject>)observer {
474 if (observer != _flutterViewControllerWillDeallocObserver) {
475 if (_flutterViewControllerWillDeallocObserver) {
476 [[NSNotificationCenter defaultCenter]
477 removeObserver:_flutterViewControllerWillDeallocObserver];
479 _flutterViewControllerWillDeallocObserver = observer;
483 - (void)notifyViewControllerDeallocated {
484 [
self.lifecycleChannel sendMessage:@"AppLifecycleState.detached"];
485 self.textInputPlugin.viewController = nil;
486 if (!
self.allowHeadlessExecution) {
487 [
self destroyContext];
488 }
else if (
self.platformView) {
489 self.platformView->SetOwnerViewController({});
491 [
self.textInputPlugin resetViewResponder];
492 _viewController = nil;
495 - (void)destroyContext {
496 [
self resetChannels];
497 self.isolateId = nil;
501 _platformViewsController = nil;
504 - (NSURL*)observatoryUrl {
505 return self.publisher.url;
508 - (NSURL*)vmServiceUrl {
509 return self.publisher.url;
512 - (void)resetChannels {
513 self.localizationChannel = nil;
514 self.navigationChannel = nil;
515 self.restorationChannel = nil;
516 self.platformChannel = nil;
517 self.platformViewsChannel = nil;
518 self.textInputChannel = nil;
519 self.undoManagerChannel = nil;
520 self.scribbleChannel = nil;
521 self.lifecycleChannel = nil;
522 self.systemChannel = nil;
523 self.settingsChannel = nil;
524 self.keyEventChannel = nil;
525 self.spellCheckChannel = nil;
528 - (void)startProfiler {
529 FML_DCHECK(!_threadHost->name_prefix.empty());
530 _profiler = std::make_shared<flutter::SamplingProfiler>(
531 _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
533 flutter::ProfilerMetricsIOS profiler_metrics;
534 return profiler_metrics.GenerateSample();
543 - (void)setUpChannels {
547 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
548 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
557 binaryMessenger:self.binaryMessenger
560 self.navigationChannel =
562 binaryMessenger:self.binaryMessenger
565 if ([_initialRoute length] > 0) {
567 [
self.navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
571 self.restorationChannel =
573 binaryMessenger:self.binaryMessenger
576 self.platformChannel =
578 binaryMessenger:self.binaryMessenger
581 self.platformViewsChannel =
583 binaryMessenger:self.binaryMessenger
586 self.textInputChannel =
588 binaryMessenger:self.binaryMessenger
591 self.undoManagerChannel =
593 binaryMessenger:self.binaryMessenger
596 self.scribbleChannel =
598 binaryMessenger:self.binaryMessenger
601 self.spellCheckChannel =
603 binaryMessenger:self.binaryMessenger
606 self.lifecycleChannel =
608 binaryMessenger:self.binaryMessenger
613 binaryMessenger:self.binaryMessenger
616 self.settingsChannel =
618 binaryMessenger:self.binaryMessenger
621 self.keyEventChannel =
623 binaryMessenger:self.binaryMessenger
627 self.textInputPlugin.indirectScribbleDelegate =
self;
628 [
self.textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
633 self.restorationPlugin =
635 restorationEnabled:self.restorationEnabled];
638 self.screenshotChannel =
640 binaryMessenger:self.binaryMessenger
643 [
self.screenshotChannel setMethodCallHandler:^(FlutterMethodCall* _Nonnull call,
644 FlutterResult _Nonnull result) {
646 if (!(strongSelf && strongSelf->_shell && strongSelf->_shell->IsSetup())) {
649 message:@"Requesting screenshot while engine is not running."
652 flutter::Rasterizer::Screenshot screenshot =
653 [strongSelf screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData base64Encode:NO];
654 if (!screenshot.data) {
656 message:@"Unable to get screenshot."
660 NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
661 length:screenshot.data->size()];
662 NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
663 NSNumber* width = @(screenshot.frame_size.fWidth);
664 NSNumber* height = @(screenshot.frame_size.fHeight);
665 return result(@[ width, height, format ?: [NSNull null], data ]);
669 - (void)maybeSetupPlatformViewChannels {
670 if (
_shell &&
self.shell.IsSetup()) {
673 [
self.platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
674 [weakSelf.platformPlugin handleMethodCall:call result:result];
677 [
self.platformViewsChannel
678 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
680 [weakSelf.platformViewsController onMethodCall:call result:result];
684 [
self.textInputChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
685 [weakSelf.textInputPlugin handleMethodCall:call result:result];
688 [
self.undoManagerChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
689 [weakSelf.undoManagerPlugin handleMethodCall:call result:result];
692 [
self.spellCheckChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
693 [weakSelf.spellCheckPlugin handleMethodCall:call result:result];
698 - (
flutter::Rasterizer::Screenshot)screenshot:(
flutter::Rasterizer::ScreenshotType)type
699 base64Encode:(
bool)base64Encode {
700 return self.shell.Screenshot(type, base64Encode);
703 - (void)launchEngine:(NSString*)entrypoint
704 libraryURI:(NSString*)libraryOrNil
705 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
707 self.shell.RunEngine([
self.dartProject runConfigurationForEntrypoint:entrypoint
708 libraryOrNil:libraryOrNil
709 entrypointArgs:entrypointArgs]);
712 - (void)setUpShell:(std::unique_ptr<
flutter::Shell>)shell
713 withVMServicePublication:(BOOL)doesVMServicePublication {
714 _shell = std::move(shell);
715 [
self setUpChannels];
716 [
self onLocaleUpdated:nil];
717 [
self updateDisplays];
719 initWithEnableVMServicePublication:doesVMServicePublication];
720 [
self maybeSetupPlatformViewChannels];
721 _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
722 : flutter::GpuAvailability::kAvailable);
725 + (BOOL)isProfilerEnabled {
726 bool profilerEnabled =
false;
727 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
728 (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
729 profilerEnabled =
true;
731 return profilerEnabled;
734 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
735 static size_t s_shellCount = 0;
736 return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
739 static flutter::ThreadHost MakeThreadHost(NSString* thread_label,
740 const flutter::Settings& settings) {
743 fml::MessageLoop::EnsureInitializedForCurrentThread();
745 uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo;
746 if (!settings.enable_impeller || !settings.merged_platform_ui_thread) {
747 threadHostType |= flutter::ThreadHost::Type::kUi;
751 threadHostType = threadHostType | flutter::ThreadHost::Type::kProfiler;
754 flutter::ThreadHost::ThreadHostConfig host_config(thread_label.UTF8String, threadHostType,
757 host_config.ui_config =
758 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
759 flutter::ThreadHost::Type::kUi, thread_label.UTF8String),
760 fml::Thread::ThreadPriority::kDisplay);
761 host_config.raster_config =
762 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
763 flutter::ThreadHost::Type::kRaster, thread_label.UTF8String),
764 fml::Thread::ThreadPriority::kRaster);
766 host_config.io_config =
767 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
768 flutter::ThreadHost::Type::kIo, thread_label.UTF8String),
769 fml::Thread::ThreadPriority::kNormal);
771 return (flutter::ThreadHost){host_config};
774 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
776 FML_DCHECK(entrypoint) <<
"Must specify entrypoint if specifying library";
777 settings->advisory_script_entrypoint = entrypoint.UTF8String;
778 settings->advisory_script_uri = libraryURI.UTF8String;
779 }
else if (entrypoint) {
780 settings->advisory_script_entrypoint = entrypoint.UTF8String;
781 settings->advisory_script_uri = std::string(
"main.dart");
783 settings->advisory_script_entrypoint = std::string(
"main");
784 settings->advisory_script_uri = std::string(
"main.dart");
788 - (BOOL)createShell:(NSString*)entrypoint
789 libraryURI:(NSString*)libraryURI
790 initialRoute:(NSString*)initialRoute {
792 FML_LOG(WARNING) <<
"This FlutterEngine was already invoked.";
796 self.initialRoute = initialRoute;
798 auto settings = [
self.dartProject settings];
799 if (initialRoute != nil) {
800 self.initialRoute = initialRoute;
801 }
else if (settings.route.empty() ==
false) {
802 self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
807 auto platformData = [
self.dartProject defaultPlatformData];
809 SetEntryPoint(&settings, entrypoint, libraryURI);
811 NSString* threadLabel = [
FlutterEngine generateThreadLabel:self.labelPrefix];
812 _threadHost = std::make_shared<flutter::ThreadHost>();
813 *_threadHost = MakeThreadHost(threadLabel, settings);
816 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
817 [weakSelf](flutter::Shell& shell) {
820 return std::unique_ptr<flutter::PlatformViewIOS>();
822 [strongSelf recreatePlatformViewsController];
823 strongSelf.platformViewsController.taskRunner =
824 shell.GetTaskRunners().GetPlatformTaskRunner();
825 return std::make_unique<flutter::PlatformViewIOS>(
826 shell, strongSelf->_renderingApi, strongSelf.platformViewsController,
827 shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
828 shell.GetIsGpuDisabledSyncSwitch());
831 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
832 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
834 fml::RefPtr<fml::TaskRunner> ui_runner;
835 if (settings.enable_impeller && settings.merged_platform_ui_thread) {
836 ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
838 ui_runner = _threadHost->ui_thread->GetTaskRunner();
840 flutter::TaskRunners task_runners(threadLabel.UTF8String,
841 fml::MessageLoop::GetCurrent().GetTaskRunner(),
842 _threadHost->raster_thread->GetTaskRunner(),
844 _threadHost->io_thread->GetTaskRunner()
847 #if APPLICATION_EXTENSION_API_ONLY
848 if (@available(iOS 13.0, *)) {
849 _isGpuDisabled =
self.viewController.flutterWindowSceneIfViewLoaded.activationState ==
850 UISceneActivationStateBackground;
858 [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
862 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
866 on_create_platform_view,
867 on_create_rasterizer,
870 if (shell ==
nullptr) {
871 FML_LOG(ERROR) <<
"Could not start a shell FlutterEngine with entrypoint: "
872 << entrypoint.UTF8String;
875 FML_LOG(INFO) <<
"Enabled VM Service Publication: " << settings.enable_vm_service_publication;
876 [
self setUpShell:std::move(shell)
877 withVMServicePublication:settings.enable_vm_service_publication];
879 [
self startProfiler];
886 - (void)updateDisplays {
891 auto vsync_waiter =
_shell->GetVsyncWaiter().lock();
892 auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
893 std::vector<std::unique_ptr<flutter::Display>> displays;
894 auto screen_size = UIScreen.mainScreen.nativeBounds.size;
895 auto scale = UIScreen.mainScreen.scale;
896 displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
897 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
898 _shell->OnDisplayUpdates(std::move(displays));
907 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
908 return [
self runWithEntrypoint:entrypoint
909 libraryURI:libraryURI
910 initialRoute:FlutterDefaultInitialRoute];
913 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
914 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
917 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
918 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
921 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
922 libraryURI:(NSString*)libraryURI
923 initialRoute:(NSString*)initialRoute {
924 return [
self runWithEntrypoint:entrypoint
925 libraryURI:libraryURI
926 initialRoute:initialRoute
930 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
931 libraryURI:(NSString*)libraryURI
932 initialRoute:(NSString*)initialRoute
933 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
934 if ([
self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
935 [
self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
941 - (void)notifyLowMemory {
943 _shell->NotifyLowMemoryWarning();
945 [
self.systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
948 #pragma mark - Text input delegate
951 updateEditingClient:(
int)client
952 withState:(NSDictionary*)state {
953 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingState"
954 arguments:@[ @(client), state ]];
958 updateEditingClient:(
int)client
959 withState:(NSDictionary*)state
960 withTag:(NSString*)tag {
961 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithTag"
962 arguments:@[ @(client), @{tag : state} ]];
966 updateEditingClient:(
int)client
967 withDelta:(NSDictionary*)delta {
968 [
self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
969 arguments:@[ @(client), delta ]];
973 updateFloatingCursor:(FlutterFloatingCursorDragState)state
974 withClient:(
int)client
975 withPosition:(NSDictionary*)position {
976 NSString* stateString;
978 case FlutterFloatingCursorDragStateStart:
979 stateString =
@"FloatingCursorDragState.start";
981 case FlutterFloatingCursorDragStateUpdate:
982 stateString =
@"FloatingCursorDragState.update";
984 case FlutterFloatingCursorDragStateEnd:
985 stateString =
@"FloatingCursorDragState.end";
988 [
self.textInputChannel invokeMethod:@"TextInputClient.updateFloatingCursor"
989 arguments:@[ @(client), stateString, position ]];
993 performAction:(FlutterTextInputAction)action
994 withClient:(
int)client {
995 NSString* actionString;
997 case FlutterTextInputActionUnspecified:
1002 actionString =
@"TextInputAction.unspecified";
1004 case FlutterTextInputActionDone:
1005 actionString =
@"TextInputAction.done";
1007 case FlutterTextInputActionGo:
1008 actionString =
@"TextInputAction.go";
1010 case FlutterTextInputActionSend:
1011 actionString =
@"TextInputAction.send";
1013 case FlutterTextInputActionSearch:
1014 actionString =
@"TextInputAction.search";
1016 case FlutterTextInputActionNext:
1017 actionString =
@"TextInputAction.next";
1019 case FlutterTextInputActionContinue:
1020 actionString =
@"TextInputAction.continueAction";
1022 case FlutterTextInputActionJoin:
1023 actionString =
@"TextInputAction.join";
1025 case FlutterTextInputActionRoute:
1026 actionString =
@"TextInputAction.route";
1028 case FlutterTextInputActionEmergencyCall:
1029 actionString =
@"TextInputAction.emergencyCall";
1031 case FlutterTextInputActionNewline:
1032 actionString =
@"TextInputAction.newline";
1035 [
self.textInputChannel invokeMethod:@"TextInputClient.performAction"
1036 arguments:@[ @(client), actionString ]];
1040 showAutocorrectionPromptRectForStart:(NSUInteger)start
1042 withClient:(
int)client {
1043 [
self.textInputChannel invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1044 arguments:@[ @(client), @(start), @(end) ]];
1048 willDismissEditMenuWithTextInputClient:(
int)client {
1049 [
self.platformChannel invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
1050 arguments:@[ @(client) ]];
1053 #pragma mark - FlutterViewEngineDelegate
1059 [
self.textInputChannel invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1063 focusElement:(UIScribbleElementIdentifier)elementIdentifier
1064 atPoint:(CGPoint)referencePoint
1069 [
self.textInputChannel
1070 invokeMethod:@"TextInputClient.focusElement"
1071 arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1076 requestElementsInRect:(CGRect)rect
1081 [
self.textInputChannel
1082 invokeMethod:@"TextInputClient.requestElementsInRect"
1083 arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1091 [
self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1098 [
self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionFinished" arguments:nil];
1102 insertTextPlaceholderWithSize:(CGSize)size
1103 withClient:(
int)client {
1107 [
self.textInputChannel invokeMethod:@"TextInputClient.insertTextPlaceholder"
1108 arguments:@[ @(client), @(size.width), @(size.height) ]];
1112 removeTextPlaceholder:(
int)client {
1116 [
self.textInputChannel invokeMethod:@"TextInputClient.removeTextPlaceholder"
1117 arguments:@[ @(client) ]];
1121 didResignFirstResponderWithTextInputClient:(
int)client {
1125 [
self.textInputChannel invokeMethod:@"TextInputClient.onConnectionClosed"
1126 arguments:@[ @(client) ]];
1147 dispatch_async(dispatch_get_main_queue(), ^(
void) {
1148 long platform_view_id = [
self.platformViewsController firstResponderPlatformViewId];
1149 if (platform_view_id == -1) {
1153 [
self.platformViewsChannel invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1157 #pragma mark - Undo Manager Delegate
1159 - (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1160 NSString* action = (direction == FlutterUndoRedoDirectionUndo) ?
@"undo" :
@"redo";
1161 [
self.undoManagerChannel invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1164 - (UIView<UITextInput>*)activeTextInputView {
1165 return [[
self textInputPlugin] textInputView];
1168 - (NSUndoManager*)undoManager {
1169 return self.viewController.undoManager;
1172 #pragma mark - Screenshot Delegate
1174 - (
flutter::Rasterizer::Screenshot)takeScreenshot:(
flutter::Rasterizer::ScreenshotType)type
1175 asBase64Encoded:(BOOL)base64Encode {
1176 FML_DCHECK(
_shell) <<
"Cannot takeScreenshot without a shell";
1177 return _shell->Screenshot(type, base64Encode);
1180 - (void)flutterViewAccessibilityDidCall {
1182 [
self ensureSemanticsEnabled];
1204 #pragma mark - FlutterBinaryMessenger
1206 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1207 [
self sendOnChannel:channel message:message binaryReply:nil];
1210 - (void)sendOnChannel:(NSString*)channel
1211 message:(NSData*)message
1213 NSParameterAssert(channel);
1215 @"Sending a message before the FlutterEngine has been run.");
1216 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
1217 (callback == nil) ?
nullptr
1218 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1222 _shell->GetTaskRunners().GetPlatformTaskRunner());
1223 std::unique_ptr<flutter::PlatformMessage> platformMessage =
1224 (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1225 : std::make_unique<flutter::PlatformMessage>(
1228 _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1238 binaryMessageHandler:
1240 return [
self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1244 setMessageHandlerOnChannel:(NSString*)channel
1247 NSParameterAssert(channel);
1249 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1250 handler, taskQueue);
1251 return _connections->AquireConnection(channel.UTF8String);
1253 NSAssert(!handler,
@"Setting a message handler before the FlutterEngine has been run.");
1261 std::string channel =
_connections->CleanupConnection(connection);
1262 if (!channel.empty()) {
1263 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
1269 #pragma mark - FlutterTextureRegistry
1272 FML_DCHECK(
self.platformView);
1273 int64_t textureId =
self.nextTextureId++;
1274 self.platformView->RegisterExternalTexture(textureId, texture);
1278 - (void)unregisterTexture:(int64_t)textureId {
1279 _shell->GetPlatformView()->UnregisterTexture(textureId);
1282 - (void)textureFrameAvailable:(int64_t)textureId {
1283 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1286 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1290 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1294 - (id<FlutterPluginRegistry>)pluginRegistry {
1298 #pragma mark - FlutterPluginRegistry
1301 NSAssert(
self.pluginPublications[pluginKey] == nil,
@"Duplicate plugin key: %@", pluginKey);
1302 self.pluginPublications[pluginKey] = [NSNull null];
1304 flutterEngine:self];
1305 self.registrars[pluginKey] = result;
1309 - (BOOL)hasPlugin:(NSString*)pluginKey {
1310 return _pluginPublications[pluginKey] != nil;
1313 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1314 return _pluginPublications[pluginKey];
1317 #pragma mark - Notifications
1319 #if APPLICATION_EXTENSION_API_ONLY
1320 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1321 [
self flutterWillEnterForeground:notification];
1324 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1325 [
self flutterDidEnterBackground:notification];
1328 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1329 [
self flutterWillEnterForeground:notification];
1332 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1333 [
self flutterDidEnterBackground:notification];
1337 - (void)flutterWillEnterForeground:(NSNotification*)notification {
1338 [
self setIsGpuDisabled:NO];
1341 - (void)flutterDidEnterBackground:(NSNotification*)notification {
1342 [
self setIsGpuDisabled:YES];
1343 [
self notifyLowMemory];
1346 - (void)onMemoryWarning:(NSNotification*)notification {
1347 [
self notifyLowMemory];
1350 - (void)setIsGpuDisabled:(BOOL)value {
1352 _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1353 : flutter::GpuAvailability::kAvailable);
1355 _isGpuDisabled = value;
1358 #pragma mark - Locale updates
1360 - (void)onLocaleUpdated:(NSNotification*)notification {
1362 NSMutableArray<NSString*>* localeData = [[NSMutableArray alloc] init];
1363 NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1364 for (NSString* localeID in preferredLocales) {
1365 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1366 NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1367 NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1368 NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1369 NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1370 if (!languageCode) {
1373 [localeData addObject:languageCode];
1374 [localeData addObject:(countryCode ? countryCode : @"")];
1375 [localeData addObject:(scriptCode ? scriptCode : @"")];
1376 [localeData addObject:(variantCode ? variantCode : @"")];
1378 if (localeData.count == 0) {
1381 [
self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1384 - (void)waitForFirstFrameSync:(NSTimeInterval)timeout
1385 callback:(NS_NOESCAPE
void (^_Nonnull)(BOOL didTimeout))callback {
1386 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1387 fml::Status status =
self.shell.WaitForFirstFrame(waitTime);
1388 callback(status.code() == fml::StatusCode::kDeadlineExceeded);
1391 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1392 callback:(
void (^_Nonnull)(BOOL didTimeout))callback {
1393 dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1394 dispatch_group_t group = dispatch_group_create();
1397 __block BOOL didTimeout = NO;
1398 dispatch_group_async(group, queue, ^{
1404 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1405 fml::Status status = strongSelf.
shell.WaitForFirstFrame(waitTime);
1406 didTimeout = status.code() == fml::StatusCode::kDeadlineExceeded;
1410 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
1424 callback(didTimeout);
1428 - (
FlutterEngine*)spawnWithEntrypoint:( NSString*)entrypoint
1429 libraryURI:( NSString*)libraryURI
1430 initialRoute:( NSString*)initialRoute
1431 entrypointArgs:( NSArray<NSString*>*)entrypointArgs {
1432 NSAssert(
_shell,
@"Spawning from an engine without a shell (possibly not run).");
1434 project:self.dartProject
1435 allowHeadlessExecution:self.allowHeadlessExecution];
1436 flutter::RunConfiguration configuration =
1437 [
self.dartProject runConfigurationForEntrypoint:entrypoint
1438 libraryOrNil:libraryURI
1439 entrypointArgs:entrypointArgs];
1446 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->
GetIosContext();
1447 FML_DCHECK(context);
1451 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
1452 [result, context](flutter::Shell& shell) {
1453 [result recreatePlatformViewsController];
1454 result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
1455 return std::make_unique<flutter::PlatformViewIOS>(
1456 shell, context, result.platformViewsController, shell.GetTaskRunners());
1459 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
1460 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
1462 std::string cppInitialRoute;
1464 cppInitialRoute = [initialRoute UTF8String];
1467 std::unique_ptr<flutter::Shell> shell =
_shell->Spawn(
1468 std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1470 result->_threadHost = _threadHost;
1472 result->_isGpuDisabled = _isGpuDisabled;
1473 [result setUpShell:std::move(shell) withVMServicePublication:NO];
1477 - (const
flutter::ThreadHost&)threadHost {
1478 return *_threadHost;
1482 return self.dartProject;
1485 - (BOOL)isUsingImpeller {
1492 NSString* _pluginKey;
1495 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
1496 self = [
super init];
1497 NSAssert(
self,
@"Super init cannot be nil");
1498 _pluginKey = [pluginKey copy];
1499 _flutterEngine = flutterEngine;
1504 return _flutterEngine.binaryMessenger;
1508 return _flutterEngine.textureRegistry;
1511 - (void)publish:(NSObject*)value {
1512 _flutterEngine.pluginPublications[_pluginKey] = value;
1515 - (void)addMethodCallDelegate:(NSObject<
FlutterPlugin>*)delegate
1522 - (void)addApplicationDelegate:(NSObject<
FlutterPlugin>*)delegate
1523 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions") {
1524 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
1526 id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1527 (id<FlutterAppLifeCycleProvider>)appDelegate;
1528 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1532 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1533 return [_flutterEngine lookupKeyForAsset:asset];
1536 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1537 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
1541 withId:(NSString*)factoryId {
1542 [
self registerViewFactory:factory
1544 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1548 withId:(NSString*)factoryId
1549 gestureRecognizersBlockingPolicy:
1551 [_flutterEngine.platformViewsController registerViewFactory:factory
1553 gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];