5 #define FML_USED_ON_EMBEDDER
11 #include "flutter/common/constants.h"
12 #include "flutter/fml/message_loop.h"
13 #include "flutter/fml/platform/darwin/platform_version.h"
14 #include "flutter/fml/trace_event.h"
15 #include "flutter/runtime/ptrace_check.h"
16 #include "flutter/shell/common/engine.h"
17 #include "flutter/shell/common/platform_view.h"
18 #include "flutter/shell/common/shell.h"
19 #include "flutter/shell/common/switches.h"
20 #include "flutter/shell/common/thread_host.h"
21 #include "flutter/shell/common/variable_refresh_rate_display.h"
41 #include "flutter/shell/profiling/sampling_profiler.h"
47 fml::Thread::SetCurrentThreadName(config);
50 switch (config.priority) {
51 case fml::Thread::ThreadPriority::BACKGROUND: {
52 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);
53 [[NSThread currentThread] setThreadPriority:0];
56 case fml::Thread::ThreadPriority::NORMAL: {
57 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
58 [[NSThread currentThread] setThreadPriority:0.5];
61 case fml::Thread::ThreadPriority::RASTER:
62 case fml::Thread::ThreadPriority::DISPLAY: {
63 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
64 [[NSThread currentThread] setThreadPriority:1.0];
67 pthread_t thread = pthread_self();
68 if (!pthread_getschedparam(thread, &policy, ¶m)) {
69 param.sched_priority = 50;
70 pthread_setschedparam(thread, policy, ¶m);
77 #pragma mark - Public exported constants
82 #pragma mark - Internal constants
100 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
101 @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
103 @property(nonatomic, readwrite, copy) NSString*
isolateId;
104 @property(nonatomic, copy) NSString* initialRoute;
105 @property(nonatomic, retain) id<NSObject> flutterViewControllerWillDeallocObserver;
107 #pragma mark - Embedder API properties
115 fml::scoped_nsobject<FlutterDartProject> _dartProject;
122 fml::scoped_nsobject<FlutterDartVMServicePublisher>
_publisher;
159 - (instancetype)init {
163 - (instancetype)initWithName:(NSString*)labelPrefix {
167 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
168 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
171 - (instancetype)initWithName:(NSString*)labelPrefix
173 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
174 return [
self initWithName:labelPrefix
176 allowHeadlessExecution:allowHeadlessExecution
177 restorationEnabled:NO];
180 - (instancetype)initWithName:(NSString*)labelPrefix
182 allowHeadlessExecution:(BOOL)allowHeadlessExecution
183 restorationEnabled:(BOOL)restorationEnabled {
185 NSAssert(
self,
@"Super init cannot be nil");
186 NSAssert(labelPrefix,
@"labelPrefix is required");
192 _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(
self);
194 if (project == nil) {
197 _dartProject.reset([project retain]);
200 _enableEmbedderAPI = _dartProject.get().settings.enable_embedder_api;
201 if (_enableEmbedderAPI) {
202 NSLog(
@"============== iOS: enable_embedder_api is on ==============");
203 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
204 FlutterEngineGetProcAddresses(&_embedderAPI);
207 if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
209 @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
210 @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
211 @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
212 @"profile and release mode apps can be launched from the home screen.");
217 _pluginPublications = [[NSMutableDictionary alloc] init];
218 _registrars = [[NSMutableDictionary alloc] init];
219 [
self recreatePlatformViewController];
225 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
226 [center addObserver:self
227 selector:@selector(onMemoryWarning:)
228 name:UIApplicationDidReceiveMemoryWarningNotification
231 #if APPLICATION_EXTENSION_API_ONLY
232 if (@available(iOS 13.0, *)) {
233 [
self setUpSceneLifecycleNotifications:center];
235 [
self setUpApplicationLifecycleNotifications:center];
238 [
self setUpApplicationLifecycleNotifications:center];
241 [center addObserver:self
242 selector:@selector(onLocaleUpdated:)
243 name:NSCurrentLocaleDidChangeNotification
249 - (void)setUpSceneLifecycleNotifications:(NSNotificationCenter*)center API_AVAILABLE(ios(13.0)) {
250 [center addObserver:self
251 selector:@selector(sceneWillEnterForeground:)
252 name:UISceneWillEnterForegroundNotification
254 [center addObserver:self
255 selector:@selector(sceneDidEnterBackground:)
256 name:UISceneDidEnterBackgroundNotification
260 - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center {
261 [center addObserver:self
262 selector:@selector(applicationWillEnterForeground:)
263 name:UIApplicationWillEnterForegroundNotification
265 [center addObserver:self
266 selector:@selector(applicationDidEnterBackground:)
267 name:UIApplicationDidEnterBackgroundNotification
271 - (void)recreatePlatformViewController {
276 - (
flutter::IOSRenderingAPI)platformViewsRenderingAPI {
283 [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) {
284 if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) {
285 NSObject<FlutterPluginRegistrar>* registrar = self.registrars[key];
286 [object detachFromEngineForRegistrar:registrar];
290 [[NSNotificationCenter defaultCenter] postNotificationName:kFlutterEngineWillDealloc
300 enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
301 registrar.flutterEngine = nil;
304 [_labelPrefix release];
305 [_initialRoute release];
306 [_pluginPublications release];
307 [_registrars release];
310 [_binaryMessenger release];
311 [_textureRegistry release];
313 [_isolateId release];
315 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
316 if (_flutterViewControllerWillDeallocObserver) {
317 [center removeObserver:_flutterViewControllerWillDeallocObserver];
318 [_flutterViewControllerWillDeallocObserver release];
320 [center removeObserver:self];
330 - (fml::WeakPtr<FlutterEngine>)getWeakPtr {
334 - (void)updateViewportMetrics:(
flutter::ViewportMetrics)viewportMetrics {
335 if (!
self.platformView) {
338 self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
341 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet {
342 if (!
self.platformView) {
345 self.platformView->DispatchPointerDataPacket(std::move(packet));
348 - (fml::WeakPtr<flutter::PlatformView>)platformView {
350 return _shell->GetPlatformView();
353 - (
flutter::PlatformViewIOS*)iosPlatformView {
358 - (fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
360 return _shell->GetTaskRunners().GetPlatformTaskRunner();
363 - (fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
365 return _shell->GetTaskRunners().GetUITaskRunner();
368 - (fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
370 return _shell->GetTaskRunners().GetRasterTaskRunner();
373 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
374 callback:(FlutterKeyEventCallback)callback
375 userData:(
void*)userData API_AVAILABLE(ios(13.4)) {
376 if (@available(iOS 13.4, *)) {
380 if (!
self.platformView) {
383 const char* character =
event.character;
385 flutter::KeyData key_data;
387 key_data.timestamp = (uint64_t)event.timestamp;
388 switch (event.type) {
389 case kFlutterKeyEventTypeUp:
390 key_data.type = flutter::KeyEventType::kUp;
392 case kFlutterKeyEventTypeDown:
393 key_data.type = flutter::KeyEventType::kDown;
395 case kFlutterKeyEventTypeRepeat:
396 key_data.type = flutter::KeyEventType::kRepeat;
399 key_data.physical =
event.physical;
400 key_data.logical =
event.logical;
401 key_data.synthesized =
event.synthesized;
403 auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
404 NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
406 auto response = ^(NSData* reply) {
407 if (callback ==
nullptr) {
410 BOOL handled = FALSE;
411 if (reply.length == 1 && *
reinterpret_cast<const uint8_t*
>(reply.bytes) == 1) {
414 callback(handled, userData);
417 [
self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
420 - (void)ensureSemanticsEnabled {
421 self.iosPlatformView->SetSemanticsEnabled(
true);
425 FML_DCHECK(
self.iosPlatformView);
427 viewController ? [viewController getWeakPtr] : fml::WeakPtr<FlutterViewController>();
429 [
self maybeSetupPlatformViewChannels];
430 [
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"];
469 [
self destroyContext];
476 [_textInputPlugin.get() resetViewResponder];
480 - (void)destroyContext {
481 [
self resetChannels];
482 self.isolateId = nil;
499 - (std::shared_ptr<flutter::FlutterPlatformViewsController>&)platformViewsController {
548 - (NSURL*)observatoryUrl {
549 return [_publisher.get() url];
552 - (NSURL*)vmServiceUrl {
553 return [_publisher.get() url];
556 - (void)resetChannels {
572 - (void)startProfiler {
575 _profiler = std::make_shared<flutter::SamplingProfiler>(
584 - (void)setUpChannels {
587 fml::WeakPtr<FlutterEngine> weakSelf = [
self getWeakPtr];
588 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
589 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
591 weakSelf.get().isolateId =
597 initWithName:
@"flutter/localization"
598 binaryMessenger:
self.binaryMessenger
602 initWithName:
@"flutter/navigation"
603 binaryMessenger:
self.binaryMessenger
606 if ([_initialRoute length] > 0) {
608 [_navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
609 [_initialRoute release];
614 initWithName:
@"flutter/restoration"
615 binaryMessenger:
self.binaryMessenger
619 initWithName:
@"flutter/platform"
620 binaryMessenger:
self.binaryMessenger
624 initWithName:
@"flutter/platform_views"
625 binaryMessenger:
self.binaryMessenger
629 initWithName:
@"flutter/textinput"
630 binaryMessenger:
self.binaryMessenger
634 initWithName:
@"flutter/undomanager"
635 binaryMessenger:
self.binaryMessenger
639 initWithName:
@"flutter/scribble"
640 binaryMessenger:
self.binaryMessenger
644 initWithName:
@"flutter/spellcheck"
645 binaryMessenger:
self.binaryMessenger
649 initWithName:
@"flutter/lifecycle"
650 binaryMessenger:
self.binaryMessenger
654 initWithName:
@"flutter/system"
655 binaryMessenger:
self.binaryMessenger
659 initWithName:
@"flutter/settings"
660 binaryMessenger:
self.binaryMessenger
664 initWithName:
@"flutter/keyevent"
665 binaryMessenger:
self.binaryMessenger
685 initWithName:
@"flutter/screenshot"
686 binaryMessenger:
self.binaryMessenger
689 [_screenshotChannel.get()
690 setMethodCallHandler:^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
691 if (!(weakSelf.get() && weakSelf.get()->_shell && weakSelf.get()->_shell->IsSetup())) {
694 message:@"Requesting screenshot while engine is not running."
697 flutter::Rasterizer::Screenshot screenshot =
698 [weakSelf.get() screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData
700 if (!screenshot.data) {
702 message:@"Unable to get screenshot."
706 NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
707 length:screenshot.data->size()];
708 NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
709 NSNumber* width = @(screenshot.frame_size.fWidth);
710 NSNumber* height = @(screenshot.frame_size.fHeight);
711 return result(@[ width, height, format ?: [NSNull null], data ]);
715 - (void)maybeSetupPlatformViewChannels {
716 if (
_shell &&
self.shell.IsSetup()) {
718 [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
722 fml::WeakPtr<FlutterEngine> weakSelf = [
self getWeakPtr];
723 [_platformViewsChannel.get()
724 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
726 weakSelf.get().platformViewsController->OnMethodCall(call, result);
731 [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
736 [_undoManagerChannel.get()
737 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
742 [_spellCheckChannel.get()
743 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
749 - (
flutter::Rasterizer::Screenshot)screenshot:(
flutter::Rasterizer::ScreenshotType)type
750 base64Encode:(
bool)base64Encode {
751 return self.shell.Screenshot(type, base64Encode);
754 - (void)launchEngine:(NSString*)entrypoint
755 libraryURI:(NSString*)libraryOrNil
756 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
758 self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
759 libraryOrNil:libraryOrNil
760 entrypointArgs:entrypointArgs]);
763 - (void)setUpShell:(std::unique_ptr<
flutter::Shell>)shell
764 withVMServicePublication:(BOOL)doesVMServicePublication {
765 _shell = std::move(shell);
766 [
self setUpChannels];
767 [
self onLocaleUpdated:nil];
768 [
self updateDisplays];
770 initWithEnableVMServicePublication:doesVMServicePublication]);
771 [
self maybeSetupPlatformViewChannels];
772 _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
773 : flutter::GpuAvailability::kAvailable);
776 + (BOOL)isProfilerEnabled {
777 bool profilerEnabled =
false;
778 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
779 (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
780 profilerEnabled =
true;
782 return profilerEnabled;
785 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
786 static size_t s_shellCount = 0;
787 return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
790 + (
flutter::ThreadHost)makeThreadHost:(NSString*)threadLabel {
793 fml::MessageLoop::EnsureInitializedForCurrentThread();
795 uint32_t threadHostType = flutter::ThreadHost::Type::UI | flutter::ThreadHost::Type::RASTER |
796 flutter::ThreadHost::Type::IO;
799 threadHostType = threadHostType | flutter::ThreadHost::Type::Profiler;
802 flutter::ThreadHost::ThreadHostConfig host_config(threadLabel.UTF8String, threadHostType,
805 host_config.ui_config =
806 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
807 flutter::ThreadHost::Type::UI, threadLabel.UTF8String),
808 fml::Thread::ThreadPriority::DISPLAY);
810 host_config.raster_config =
811 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
812 flutter::ThreadHost::Type::RASTER, threadLabel.UTF8String),
813 fml::Thread::ThreadPriority::RASTER);
815 host_config.io_config =
816 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
817 flutter::ThreadHost::Type::IO, threadLabel.UTF8String),
818 fml::Thread::ThreadPriority::NORMAL);
820 return (flutter::ThreadHost){host_config};
823 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
825 FML_DCHECK(entrypoint) <<
"Must specify entrypoint if specifying library";
826 settings->advisory_script_entrypoint = entrypoint.UTF8String;
827 settings->advisory_script_uri = libraryURI.UTF8String;
828 }
else if (entrypoint) {
829 settings->advisory_script_entrypoint = entrypoint.UTF8String;
830 settings->advisory_script_uri = std::string(
"main.dart");
832 settings->advisory_script_entrypoint = std::string(
"main");
833 settings->advisory_script_uri = std::string(
"main.dart");
837 - (BOOL)createShell:(NSString*)entrypoint
838 libraryURI:(NSString*)libraryURI
839 initialRoute:(NSString*)initialRoute {
841 FML_LOG(WARNING) <<
"This FlutterEngine was already invoked.";
845 self.initialRoute = initialRoute;
847 auto settings = [_dartProject.get() settings];
848 if (initialRoute != nil) {
849 self.initialRoute = initialRoute;
850 }
else if (settings.route.empty() ==
false) {
851 self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
856 auto platformData = [_dartProject.get() defaultPlatformData];
858 SetEntryPoint(&settings, entrypoint, libraryURI);
860 NSString* threadLabel = [
FlutterEngine generateThreadLabel:_labelPrefix];
861 _threadHost = std::make_shared<flutter::ThreadHost>();
866 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
867 [
self](flutter::Shell& shell) {
868 [
self recreatePlatformViewController];
869 return std::make_unique<flutter::PlatformViewIOS>(
870 shell,
self->_renderingApi,
self->_platformViewsController, shell.GetTaskRunners(),
871 shell.GetConcurrentWorkerTaskRunner(), shell.GetIsGpuDisabledSyncSwitch());
874 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
875 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
877 flutter::TaskRunners task_runners(threadLabel.UTF8String,
878 fml::MessageLoop::GetCurrent().GetTaskRunner(),
884 #if APPLICATION_EXTENSION_API_ONLY
885 if (@available(iOS 13.0, *)) {
886 _isGpuDisabled =
self.viewController.flutterWindowSceneIfViewLoaded.activationState ==
887 UISceneActivationStateBackground;
895 [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
899 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
903 on_create_platform_view,
904 on_create_rasterizer,
907 if (shell ==
nullptr) {
908 FML_LOG(ERROR) <<
"Could not start a shell FlutterEngine with entrypoint: "
909 << entrypoint.UTF8String;
912 FML_LOG(INFO) <<
"Enabled VM Service Publication: " << settings.enable_vm_service_publication;
913 [
self setUpShell:std::move(shell)
914 withVMServicePublication:settings.enable_vm_service_publication];
916 [
self startProfiler];
923 - (void)updateDisplays {
928 auto vsync_waiter =
_shell->GetVsyncWaiter().lock();
929 auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
930 std::vector<std::unique_ptr<flutter::Display>> displays;
931 auto screen_size = UIScreen.mainScreen.nativeBounds.size;
932 auto scale = UIScreen.mainScreen.scale;
933 displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
934 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
935 _shell->OnDisplayUpdates(std::move(displays));
944 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
945 return [
self runWithEntrypoint:entrypoint
946 libraryURI:libraryURI
947 initialRoute:FlutterDefaultInitialRoute];
950 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
951 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
954 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
955 return [
self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
958 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
959 libraryURI:(NSString*)libraryURI
960 initialRoute:(NSString*)initialRoute {
961 return [
self runWithEntrypoint:entrypoint
962 libraryURI:libraryURI
963 initialRoute:initialRoute
967 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
968 libraryURI:(NSString*)libraryURI
969 initialRoute:(NSString*)initialRoute
970 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
971 if ([
self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
972 [
self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
978 - (void)notifyLowMemory {
980 _shell->NotifyLowMemoryWarning();
982 [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
985 #pragma mark - Text input delegate
988 updateEditingClient:(
int)client
989 withState:(NSDictionary*)state {
990 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState"
991 arguments:@[ @(client), state ]];
995 updateEditingClient:(
int)client
996 withState:(NSDictionary*)state
997 withTag:(NSString*)tag {
998 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag"
999 arguments:@[ @(client), @{tag : state} ]];
1003 updateEditingClient:(
int)client
1004 withDelta:(NSDictionary*)delta {
1005 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
1006 arguments:@[ @(client), delta ]];
1010 updateFloatingCursor:(FlutterFloatingCursorDragState)state
1011 withClient:(
int)client
1012 withPosition:(NSDictionary*)position {
1013 NSString* stateString;
1015 case FlutterFloatingCursorDragStateStart:
1016 stateString =
@"FloatingCursorDragState.start";
1018 case FlutterFloatingCursorDragStateUpdate:
1019 stateString =
@"FloatingCursorDragState.update";
1021 case FlutterFloatingCursorDragStateEnd:
1022 stateString =
@"FloatingCursorDragState.end";
1025 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor"
1026 arguments:@[ @(client), stateString, position ]];
1030 performAction:(FlutterTextInputAction)action
1031 withClient:(
int)client {
1032 NSString* actionString;
1034 case FlutterTextInputActionUnspecified:
1039 actionString =
@"TextInputAction.unspecified";
1041 case FlutterTextInputActionDone:
1042 actionString =
@"TextInputAction.done";
1044 case FlutterTextInputActionGo:
1045 actionString =
@"TextInputAction.go";
1047 case FlutterTextInputActionSend:
1048 actionString =
@"TextInputAction.send";
1050 case FlutterTextInputActionSearch:
1051 actionString =
@"TextInputAction.search";
1053 case FlutterTextInputActionNext:
1054 actionString =
@"TextInputAction.next";
1056 case FlutterTextInputActionContinue:
1057 actionString =
@"TextInputAction.continueAction";
1059 case FlutterTextInputActionJoin:
1060 actionString =
@"TextInputAction.join";
1062 case FlutterTextInputActionRoute:
1063 actionString =
@"TextInputAction.route";
1065 case FlutterTextInputActionEmergencyCall:
1066 actionString =
@"TextInputAction.emergencyCall";
1068 case FlutterTextInputActionNewline:
1069 actionString =
@"TextInputAction.newline";
1072 [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction"
1073 arguments:@[ @(client), actionString ]];
1077 showAutocorrectionPromptRectForStart:(NSUInteger)start
1079 withClient:(
int)client {
1080 [_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1081 arguments:@[ @(client), @(start), @(end) ]];
1084 #pragma mark - FlutterViewEngineDelegate
1090 [_textInputChannel.get() invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1094 focusElement:(UIScribbleElementIdentifier)elementIdentifier
1095 atPoint:(CGPoint)referencePoint
1100 [_textInputChannel.get()
1101 invokeMethod:@"TextInputClient.focusElement"
1102 arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1107 requestElementsInRect:(CGRect)rect
1112 [_textInputChannel.get()
1113 invokeMethod:@"TextInputClient.requestElementsInRect"
1114 arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1122 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1129 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionFinished"
1134 insertTextPlaceholderWithSize:(CGSize)size
1135 withClient:(
int)client {
1139 [_textInputChannel.get() invokeMethod:@"TextInputClient.insertTextPlaceholder"
1140 arguments:@[ @(client), @(size.width), @(size.height) ]];
1144 removeTextPlaceholder:(
int)client {
1148 [_textInputChannel.get() invokeMethod:@"TextInputClient.removeTextPlaceholder"
1149 arguments:@[ @(client) ]];
1153 didResignFirstResponderWithTextInputClient:(
int)client {
1157 [_textInputChannel.get() invokeMethod:@"TextInputClient.onConnectionClosed"
1158 arguments:@[ @(client) ]];
1179 dispatch_async(dispatch_get_main_queue(), ^(
void) {
1180 long platform_view_id =
self.platformViewsController->FindFirstResponderPlatformViewId();
1181 if (platform_view_id == -1) {
1185 [_platformViewsChannel.get() invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1189 #pragma mark - Undo Manager Delegate
1192 handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1193 NSString*
action = (direction == FlutterUndoRedoDirectionUndo) ?
@"undo" :
@"redo";
1194 [_undoManagerChannel.get() invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1197 #pragma mark - Screenshot Delegate
1199 - (
flutter::Rasterizer::Screenshot)takeScreenshot:(
flutter::Rasterizer::ScreenshotType)type
1200 asBase64Encoded:(BOOL)base64Encode {
1201 FML_DCHECK(
_shell) <<
"Cannot takeScreenshot without a shell";
1202 return _shell->Screenshot(type, base64Encode);
1205 - (void)flutterViewAccessibilityDidCall {
1207 [
self ensureSemanticsEnabled];
1225 [_binaryMessenger release];
1230 #pragma mark - FlutterBinaryMessenger
1232 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1233 [
self sendOnChannel:channel message:message binaryReply:nil];
1236 - (void)sendOnChannel:(NSString*)channel
1237 message:(NSData*)message
1239 NSParameterAssert(channel);
1241 @"Sending a message before the FlutterEngine has been run.");
1242 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
1243 (callback == nil) ?
nullptr
1244 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1248 _shell->GetTaskRunners().GetPlatformTaskRunner());
1249 std::unique_ptr<flutter::PlatformMessage> platformMessage =
1250 (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1251 : std::make_unique<flutter::PlatformMessage>(
1254 _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1264 binaryMessageHandler:
1266 return [
self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1270 setMessageHandlerOnChannel:(NSString*)channel
1273 NSParameterAssert(channel);
1275 self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1276 handler, taskQueue);
1277 return _connections->AquireConnection(channel.UTF8String);
1279 NSAssert(!handler,
@"Setting a message handler before the FlutterEngine has been run.");
1287 std::string channel =
_connections->CleanupConnection(connection);
1288 if (!channel.empty()) {
1289 self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
1295 #pragma mark - FlutterTextureRegistry
1299 self.iosPlatformView->RegisterExternalTexture(textureId, texture);
1303 - (void)unregisterTexture:(int64_t)textureId {
1304 _shell->GetPlatformView()->UnregisterTexture(textureId);
1307 - (void)textureFrameAvailable:(int64_t)textureId {
1308 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1311 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1315 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1319 - (
id<FlutterPluginRegistry>)pluginRegistry {
1323 #pragma mark - FlutterPluginRegistry
1326 NSAssert(
self.pluginPublications[pluginKey] == nil,
@"Duplicate plugin key: %@", pluginKey);
1327 self.pluginPublications[pluginKey] = [NSNull null];
1329 flutterEngine:self];
1330 self.registrars[pluginKey] = result;
1331 return [result autorelease];
1334 - (BOOL)hasPlugin:(NSString*)pluginKey {
1335 return _pluginPublications[pluginKey] != nil;
1338 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1339 return _pluginPublications[pluginKey];
1342 #pragma mark - Notifications
1344 #if APPLICATION_EXTENSION_API_ONLY
1345 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1346 [
self flutterWillEnterForeground:notification];
1349 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1350 [
self flutterDidEnterBackground:notification];
1353 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1354 [
self flutterWillEnterForeground:notification];
1357 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1358 [
self flutterDidEnterBackground:notification];
1362 - (void)flutterWillEnterForeground:(NSNotification*)notification {
1363 [
self setIsGpuDisabled:NO];
1366 - (void)flutterDidEnterBackground:(NSNotification*)notification {
1367 [
self setIsGpuDisabled:YES];
1368 [
self notifyLowMemory];
1371 - (void)onMemoryWarning:(NSNotification*)notification {
1372 [
self notifyLowMemory];
1375 - (void)setIsGpuDisabled:(BOOL)value {
1377 _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1378 : flutter::GpuAvailability::kAvailable);
1380 _isGpuDisabled = value;
1383 #pragma mark - Locale updates
1385 - (void)onLocaleUpdated:(NSNotification*)notification {
1387 NSMutableArray<NSString*>* localeData = [[[NSMutableArray alloc] init] autorelease];
1388 NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1389 for (NSString* localeID in preferredLocales) {
1390 NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
1391 NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1392 NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1393 NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1394 NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1395 if (!languageCode) {
1398 [localeData addObject:languageCode];
1399 [localeData addObject:(countryCode ? countryCode : @"")];
1400 [localeData addObject:(scriptCode ? scriptCode : @"")];
1401 [localeData addObject:(variantCode ? variantCode : @"")];
1403 if (localeData.count == 0) {
1406 [
self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1409 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1410 callback:(
void (^_Nonnull)(BOOL didTimeout))callback {
1411 dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1412 dispatch_async(queue, ^{
1413 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1415 self.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
1416 dispatch_async(dispatch_get_main_queue(), ^{
1417 callback(didTimeout);
1422 - (
FlutterEngine*)spawnWithEntrypoint:( NSString*)entrypoint
1423 libraryURI:( NSString*)libraryURI
1424 initialRoute:( NSString*)initialRoute
1425 entrypointArgs:( NSArray<NSString*>*)entrypointArgs {
1426 NSAssert(
_shell,
@"Spawning from an engine without a shell (possibly not run).");
1428 project:_dartProject.get()
1429 allowHeadlessExecution:_allowHeadlessExecution];
1430 flutter::RunConfiguration configuration =
1431 [_dartProject.get() runConfigurationForEntrypoint:entrypoint
1432 libraryOrNil:libraryURI
1433 entrypointArgs:entrypointArgs];
1440 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->
GetIosContext();
1441 FML_DCHECK(context);
1445 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
1446 [result, context](flutter::Shell& shell) {
1447 [result recreatePlatformViewController];
1448 return std::make_unique<flutter::PlatformViewIOS>(
1449 shell, context, result->_platformViewsController, shell.GetTaskRunners());
1452 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
1453 [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell); };
1455 std::string cppInitialRoute;
1457 cppInitialRoute = [initialRoute UTF8String];
1460 std::unique_ptr<flutter::Shell> shell =
_shell->Spawn(
1461 std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1466 result->_isGpuDisabled = _isGpuDisabled;
1467 [result setUpShell:std::move(shell) withVMServicePublication:NO];
1468 return [result autorelease];
1471 - (const
flutter::ThreadHost&)threadHost {
1476 return _dartProject.get();
1479 - (BOOL)isUsingImpeller {
1486 NSString* _pluginKey;
1489 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
1490 self = [
super init];
1491 NSAssert(
self,
@"Super init cannot be nil");
1492 _pluginKey = [pluginKey copy];
1493 _flutterEngine = flutterEngine;
1498 [_pluginKey release];
1503 return _flutterEngine.binaryMessenger;
1507 return _flutterEngine.textureRegistry;
1510 - (void)publish:(NSObject*)value {
1511 _flutterEngine.pluginPublications[_pluginKey] = value;
1514 - (void)addMethodCallDelegate:(NSObject<
FlutterPlugin>*)delegate
1521 - (void)addApplicationDelegate:(NSObject<
FlutterPlugin>*)delegate
1522 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions") {
1523 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
1525 id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1526 (id<FlutterAppLifeCycleProvider>)appDelegate;
1527 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1531 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1532 return [_flutterEngine lookupKeyForAsset:asset];
1535 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1536 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
1540 withId:(NSString*)factoryId {
1541 [
self registerViewFactory:factory
1543 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1547 withId:(NSString*)factoryId
1548 gestureRecognizersBlockingPolicy:
1550 [_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
1551 gestureRecognizersBlockingPolicy);