| Flutter iOS Embedder |

FlutterEngine.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <UIKit/UIKit.h>
6 #include "common/settings.h"
7 #define FML_USED_ON_EMBEDDER
8
9 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
10
11 #include <memory>
12
13 #include "flutter/common/constants.h"
14 #include "flutter/fml/message_loop.h"
15 #include "flutter/fml/platform/darwin/platform_version.h"
16 #include "flutter/fml/trace_event.h"
17 #include "flutter/runtime/ptrace_check.h"
18 #include "flutter/shell/common/engine.h"
19 #include "flutter/shell/common/platform_view.h"
20 #include "flutter/shell/common/shell.h"
21 #include "flutter/shell/common/switches.h"
22 #include "flutter/shell/common/thread_host.h"
23 #include "flutter/shell/common/variable_refresh_rate_display.h"
24 #import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
25 #import "flutter/shell/platform/darwin/common/command_line.h"
26 #import "flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h"
27 #import "flutter/shell/platform/darwin/ios/InternalFlutterSwift/InternalFlutterSwift.h"
28 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
29 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartVMServicePublisher.h"
30 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterIndirectScribbleDelegate.h"
31 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
32 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSharedApplication.h"
33 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSpellCheckPlugin.h"
34 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
35 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextureRegistryRelay.h"
36 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerDelegate.h"
37 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.h"
38 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
39 #import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
40 #import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
41 #import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
42 #import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
43 #import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
44 #import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
45 #include "flutter/shell/profiling/sampling_profiler.h"
46
47 FLUTTER_ASSERT_ARC
48
49 /// Inheriting ThreadConfigurer and use iOS platform thread API to configure the thread priorities
50 /// Using iOS platform thread API to configure thread priority
51 static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
52 // set thread name
53 fml::Thread::SetCurrentThreadName(config);
54
55 // set thread priority
56 switch (config.priority) {
57 case fml::Thread::ThreadPriority::kBackground: {
58 pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);
59 [[NSThread currentThread] setThreadPriority:0];
60 break;
61 }
62 case fml::Thread::ThreadPriority::kNormal: {
63 pthread_set_qos_class_self_np(QOS_CLASS_DEFAULT, 0);
64 [[NSThread currentThread] setThreadPriority:0.5];
65 break;
66 }
67 case fml::Thread::ThreadPriority::kRaster:
68 case fml::Thread::ThreadPriority::kDisplay: {
69 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
70 [[NSThread currentThread] setThreadPriority:1.0];
71 sched_param param;
72 int policy;
73 pthread_t thread = pthread_self();
74 if (!pthread_getschedparam(thread, &policy, &param)) {
75 param.sched_priority = 50;
76 pthread_setschedparam(thread, policy, &param);
77 }
78 break;
79 }
80 }
81 }
82
83 #pragma mark - Public exported constants
84
85 NSString* const FlutterDefaultDartEntrypoint = nil;
86 NSString* const FlutterDefaultInitialRoute = nil;
87
88 #pragma mark - Internal constants
89
90 NSString* const kFlutterKeyDataChannel = @"flutter/keydata";
91 static constexpr int kNumProfilerSamplesPerSec = 5;
92
93 @interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
94 @property(nonatomic, weak) FlutterEngine* flutterEngine;
95 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine;
96 @end
97
98 @interface FlutterEngine () <FlutterIndirectScribbleDelegate,
99 FlutterUndoManagerDelegate,
100 FlutterTextInputDelegate,
101 FlutterBinaryMessenger,
102 FlutterTextureRegistry>
103
104 #pragma mark - Properties
105
106 @property(nonatomic, readonly) FlutterDartProject* dartProject;
107 @property(nonatomic, readonly, copy) NSString* labelPrefix;
108 @property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
109 @property(nonatomic, readonly, assign) BOOL restorationEnabled;
110
111 @property(nonatomic, strong) FlutterPlatformViewsController* platformViewsController;
112
113 // Maintains a dictionary of plugin names that have registered with the engine. Used by
114 // FlutterEngineRegistrar to implement a FlutterPluginRegistrar.
115 @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
116 @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
117
118 @property(nonatomic, readwrite, copy) NSString* isolateId;
119 @property(nonatomic, copy) NSString* initialRoute;
120 @property(nonatomic, strong) id<NSObject> flutterViewControllerWillDeallocObserver;
121 @property(nonatomic, strong) FlutterDartVMServicePublisher* publisher;
122 @property(nonatomic, strong) FlutterConnectionCollection* connections;
123 @property(nonatomic, assign) int64_t nextTextureId;
124
125 #pragma mark - Channel properties
126
127 @property(nonatomic, strong) FlutterPlatformPlugin* platformPlugin;
128 @property(nonatomic, strong) FlutterTextInputPlugin* textInputPlugin;
129 @property(nonatomic, strong) FlutterUndoManagerPlugin* undoManagerPlugin;
130 @property(nonatomic, strong) FlutterSpellCheckPlugin* spellCheckPlugin;
131 @property(nonatomic, strong) FlutterRestorationPlugin* restorationPlugin;
132 @property(nonatomic, strong) FlutterMethodChannel* localizationChannel;
133 @property(nonatomic, strong) FlutterMethodChannel* navigationChannel;
134 @property(nonatomic, strong) FlutterMethodChannel* restorationChannel;
135 @property(nonatomic, strong) FlutterMethodChannel* platformChannel;
136 @property(nonatomic, strong) FlutterMethodChannel* platformViewsChannel;
137 @property(nonatomic, strong) FlutterMethodChannel* textInputChannel;
138 @property(nonatomic, strong) FlutterMethodChannel* undoManagerChannel;
139 @property(nonatomic, strong) FlutterMethodChannel* scribbleChannel;
140 @property(nonatomic, strong) FlutterMethodChannel* spellCheckChannel;
141 @property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel;
142 @property(nonatomic, strong) FlutterBasicMessageChannel* systemChannel;
143 @property(nonatomic, strong) FlutterBasicMessageChannel* settingsChannel;
144 @property(nonatomic, strong) FlutterBasicMessageChannel* keyEventChannel;
145 @property(nonatomic, strong) FlutterMethodChannel* screenshotChannel;
146
147 #pragma mark - Embedder API properties
148
149 @property(nonatomic, assign) BOOL enableEmbedderAPI;
150 // Function pointers for interacting with the embedder.h API.
151 @property(nonatomic) FlutterEngineProcTable& embedderAPI;
152
153 @end
154
155 @implementation FlutterEngine {
156 std::shared_ptr<flutter::ThreadHost> _threadHost;
157 std::unique_ptr<flutter::Shell> _shell;
158
159 flutter::IOSRenderingAPI _renderingApi;
160 std::shared_ptr<flutter::SamplingProfiler> _profiler;
161
162 FlutterBinaryMessengerRelay* _binaryMessenger;
163 FlutterTextureRegistryRelay* _textureRegistry;
164 }
165
166 - (int64_t)engineIdentifier {
167 return reinterpret_cast<int64_t>((__bridge void*)self);
168 }
169
170 - (instancetype)init {
171 return [self initWithName:@"FlutterEngine" project:nil allowHeadlessExecution:YES];
172 }
173
174 - (instancetype)initWithName:(NSString*)labelPrefix {
175 return [self initWithName:labelPrefix project:nil allowHeadlessExecution:YES];
176 }
177
178 - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
179 return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
180 }
181
182 - (instancetype)initWithName:(NSString*)labelPrefix
183 project:(FlutterDartProject*)project
184 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
185 return [self initWithName:labelPrefix
186 project:project
187 allowHeadlessExecution:allowHeadlessExecution
188 restorationEnabled:NO];
189 }
190
191 - (instancetype)initWithName:(NSString*)labelPrefix
192 project:(FlutterDartProject*)project
193 allowHeadlessExecution:(BOOL)allowHeadlessExecution
194 restorationEnabled:(BOOL)restorationEnabled {
195 self = [super init];
196 NSAssert(self, @"Super init cannot be nil");
197 NSAssert(labelPrefix, @"labelPrefix is required");
198
199 _restorationEnabled = restorationEnabled;
200 _allowHeadlessExecution = allowHeadlessExecution;
201 _labelPrefix = [labelPrefix copy];
202 _dartProject = project ?: [[FlutterDartProject alloc] init];
203
204 _enableEmbedderAPI = _dartProject.settings.enable_embedder_api;
205 if (_enableEmbedderAPI) {
206 NSLog(@"============== iOS: enable_embedder_api is on ==============");
207 _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
208 FlutterEngineGetProcAddresses(&_embedderAPI);
209 }
210
211 if (!EnableTracingIfNecessary(_dartProject.settings)) {
212 NSLog(
213 @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
214 @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
215 @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
216 @"profile and release mode apps can be launched from the home screen.");
217 return nil;
218 }
219
220 _pluginPublications = [[NSMutableDictionary alloc] init];
221 _registrars = [[NSMutableDictionary alloc] init];
222 [self recreatePlatformViewsController];
223 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
224 _textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
225 _connections = [[FlutterConnectionCollection alloc] init];
226
227 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
228 [center addObserver:self
229 selector:@selector(onMemoryWarning:)
230 name:UIApplicationDidReceiveMemoryWarningNotification
231 object:nil];
232
233 [self setUpLifecycleNotifications:center];
234
235 [center addObserver:self
236 selector:@selector(onLocaleUpdated:)
237 name:NSCurrentLocaleDidChangeNotification
238 object:nil];
239
240 return self;
241 }
242
243 + (FlutterEngine*)engineForIdentifier:(int64_t)identifier {
244 NSAssert([[NSThread currentThread] isMainThread], @"Must be called on the main thread.");
245 return (__bridge FlutterEngine*)reinterpret_cast<void*>(identifier);
246 }
247
248 - (void)setUpLifecycleNotifications:(NSNotificationCenter*)center {
249 // If the application is not available, use the scene for lifecycle notifications if available.
250 if (!FlutterSharedApplication.isAvailable) {
251 [center addObserver:self
252 selector:@selector(sceneWillEnterForeground:)
253 name:UISceneWillEnterForegroundNotification
254 object:nil];
255 [center addObserver:self
256 selector:@selector(sceneDidEnterBackground:)
257 name:UISceneDidEnterBackgroundNotification
258 object:nil];
259 return;
260 }
261 [center addObserver:self
262 selector:@selector(applicationWillEnterForeground:)
263 name:UIApplicationWillEnterForegroundNotification
264 object:nil];
265 [center addObserver:self
266 selector:@selector(applicationDidEnterBackground:)
267 name:UIApplicationDidEnterBackgroundNotification
268 object:nil];
269 }
270
271 - (void)recreatePlatformViewsController {
272 _renderingApi = flutter::GetRenderingAPIForProcess(/*force_software=*/false);
273 _platformViewsController = [[FlutterPlatformViewsController alloc] init];
274 }
275
276 - (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
277 return _renderingApi;
278 }
279
280 - (void)dealloc {
281 /// Notify plugins of dealloc. This should happen first in dealloc since the
282 /// plugins may be talking to things like the binaryMessenger.
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];
287 }
288 }];
289
290 // nil out weak references.
291 // TODO(cbracken): https://github.com/flutter/flutter/issues/156222
292 // Ensure that FlutterEngineRegistrar is using weak pointers, then eliminate this code.
293 [_registrars
294 enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
295 registrar.flutterEngine = nil;
296 }];
297
298 _binaryMessenger.parent = nil;
299 _textureRegistry.parent = nil;
300
301 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
302 if (_flutterViewControllerWillDeallocObserver) {
303 [center removeObserver:_flutterViewControllerWillDeallocObserver];
304 }
305 [center removeObserver:self];
306 }
307
308 - (flutter::Shell&)shell {
309 FML_DCHECK(_shell);
310 return *_shell;
311 }
312
313 - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics {
314 if (!self.platformView) {
315 return;
316 }
317 self.platformView->SetViewportMetrics(flutter::kFlutterImplicitViewId, viewportMetrics);
318 }
319
320 - (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet {
321 if (!self.platformView) {
322 return;
323 }
324 self.platformView->DispatchPointerDataPacket(std::move(packet));
325 }
326
327 - (void)installFirstFrameCallback:(void (^)(void))block {
328 if (!self.platformView) {
329 return;
330 }
331
332 __weak FlutterEngine* weakSelf = self;
333 self.platformView->SetNextFrameCallback([weakSelf, block] {
334 FlutterEngine* strongSelf = weakSelf;
335 if (!strongSelf) {
336 return;
337 }
338 FML_DCHECK(strongSelf.platformTaskRunner);
339 FML_DCHECK(strongSelf.rasterTaskRunner);
340 FML_DCHECK(strongSelf.rasterTaskRunner->RunsTasksOnCurrentThread());
341 // Get callback on raster thread and jump back to platform thread.
342 strongSelf.platformTaskRunner->PostTask([block]() { block(); });
343 });
344 }
345
346 - (void)enableSemantics:(BOOL)enabled withFlags:(int64_t)flags {
347 if (!self.platformView) {
348 return;
349 }
350 self.platformView->SetSemanticsEnabled(enabled);
351 self.platformView->SetAccessibilityFeatures(flags);
352 }
353
354 - (void)notifyViewCreated {
355 if (!self.platformView) {
356 return;
357 }
358 self.platformView->NotifyCreated();
359 }
360
361 - (void)notifyViewDestroyed {
362 if (!self.platformView) {
363 return;
364 }
365 self.platformView->NotifyDestroyed();
366 }
367
368 - (flutter::PlatformViewIOS*)platformView {
369 if (!_shell) {
370 return nullptr;
371 }
372 return static_cast<flutter::PlatformViewIOS*>(_shell->GetPlatformView().get());
373 }
374
375 - (fml::RefPtr<fml::TaskRunner>)platformTaskRunner {
376 if (!_shell) {
377 return {};
378 }
379 return _shell->GetTaskRunners().GetPlatformTaskRunner();
380 }
381
382 - (fml::RefPtr<fml::TaskRunner>)uiTaskRunner {
383 if (!_shell) {
384 return {};
385 }
386 return _shell->GetTaskRunners().GetUITaskRunner();
387 }
388
389 - (fml::RefPtr<fml::TaskRunner>)rasterTaskRunner {
390 if (!_shell) {
391 return {};
392 }
393 return _shell->GetTaskRunners().GetRasterTaskRunner();
394 }
395
396 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
397 callback:(FlutterKeyEventCallback)callback
398 userData:(void*)userData API_AVAILABLE(ios(13.4)) {
399 if (@available(iOS 13.4, *)) {
400 } else {
401 return;
402 }
403 if (!self.platformView) {
404 return;
405 }
406 const char* character = event.character;
407
408 flutter::KeyData key_data;
409 key_data.Clear();
410 key_data.timestamp = (uint64_t)event.timestamp;
411 switch (event.type) {
412 case kFlutterKeyEventTypeUp:
413 key_data.type = flutter::KeyEventType::kUp;
414 break;
415 case kFlutterKeyEventTypeDown:
416 key_data.type = flutter::KeyEventType::kDown;
417 break;
418 case kFlutterKeyEventTypeRepeat:
419 key_data.type = flutter::KeyEventType::kRepeat;
420 break;
421 }
422 key_data.physical = event.physical;
423 key_data.logical = event.logical;
424 key_data.synthesized = event.synthesized;
425
426 auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
427 NSData* message = [NSData dataWithBytes:packet->data().data() length:packet->data().size()];
428
429 auto response = ^(NSData* reply) {
430 if (callback == nullptr) {
431 return;
432 }
433 BOOL handled = FALSE;
434 if (reply.length == 1 && *reinterpret_cast<const uint8_t*>(reply.bytes) == 1) {
435 handled = TRUE;
436 }
437 callback(handled, userData);
438 };
439
440 [self sendOnChannel:kFlutterKeyDataChannel message:message binaryReply:response];
441 }
442
443 - (void)ensureSemanticsEnabled {
444 if (!self.platformView) {
445 return;
446 }
447 self.platformView->SetSemanticsEnabled(true);
448 }
449
450 - (void)setViewController:(FlutterViewController*)viewController {
451 FML_DCHECK(self.platformView);
452 _viewController = viewController;
453 self.platformView->SetOwnerViewController(_viewController);
454 [self maybeSetupPlatformViewChannels];
455 [self updateDisplays];
456 self.textInputPlugin.viewController = viewController;
457
458 if (viewController) {
459 __weak __block FlutterEngine* weakSelf = self;
460 self.flutterViewControllerWillDeallocObserver =
461 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
462 object:viewController
463 queue:[NSOperationQueue mainQueue]
464 usingBlock:^(NSNotification* note) {
465 [weakSelf notifyViewControllerDeallocated];
466 }];
467 } else {
468 self.flutterViewControllerWillDeallocObserver = nil;
469 [self notifyLowMemory];
470 }
471 }
472
473 - (void)attachView {
474 FML_DCHECK(self.platformView);
475 self.platformView->attachView();
476 }
477
478 - (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
479 if (observer != _flutterViewControllerWillDeallocObserver) {
480 if (_flutterViewControllerWillDeallocObserver) {
481 [[NSNotificationCenter defaultCenter]
482 removeObserver:_flutterViewControllerWillDeallocObserver];
483 }
484 _flutterViewControllerWillDeallocObserver = observer;
485 }
486 }
487
488 - (void)notifyViewControllerDeallocated {
489 [self.lifecycleChannel sendMessage:@"AppLifecycleState.detached"];
490 self.textInputPlugin.viewController = nil;
491 if (!self.allowHeadlessExecution) {
492 [self destroyContext];
493 } else if (self.platformView) {
494 self.platformView->SetOwnerViewController({});
495 }
496 [self.textInputPlugin resetViewResponder];
497 _viewController = nil;
498 }
499
500 - (void)destroyContext {
501 [self resetChannels];
502 self.isolateId = nil;
503 _shell.reset();
504 _profiler.reset();
505 _threadHost.reset();
506 _platformViewsController = nil;
507 }
508
509 - (NSURL*)vmServiceUrl {
510 return self.publisher.url;
511 }
512
513 - (void)resetChannels {
514 self.localizationChannel = nil;
515 self.navigationChannel = nil;
516 self.restorationChannel = nil;
517 self.platformChannel = nil;
518 self.platformViewsChannel = nil;
519 self.textInputChannel = nil;
520 self.undoManagerChannel = nil;
521 self.scribbleChannel = nil;
522 self.lifecycleChannel = nil;
523 self.systemChannel = nil;
524 self.settingsChannel = nil;
525 self.keyEventChannel = nil;
526 self.spellCheckChannel = nil;
527 }
528
529 - (void)startProfiler {
530 FML_DCHECK(!_threadHost->name_prefix.empty());
531 _profiler = std::make_shared<flutter::SamplingProfiler>(
532 _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
533 []() {
534 flutter::ProfilerMetricsIOS profiler_metrics;
535 return profiler_metrics.GenerateSample();
536 },
537 kNumProfilerSamplesPerSec);
538 _profiler->Start();
539 }
540
541 // If you add a channel, be sure to also update `resetChannels`.
542 // Channels get a reference to the engine, and therefore need manual
543 // cleanup for proper collection.
544 - (void)setUpChannels {
545 // This will be invoked once the shell is done setting up and the isolate ID
546 // for the UI isolate is available.
547 __weak FlutterEngine* weakSelf = self;
548 [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
549 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
550 if (weakSelf) {
551 weakSelf.isolateId =
552 [[FlutterStringCodec sharedInstance] decode:message];
553 }
554 }];
555
556 self.localizationChannel =
557 [[FlutterMethodChannel alloc] initWithName:@"flutter/localization"
558 binaryMessenger:self.binaryMessenger
559 codec:[FlutterJSONMethodCodec sharedInstance]];
560
561 self.navigationChannel =
562 [[FlutterMethodChannel alloc] initWithName:@"flutter/navigation"
563 binaryMessenger:self.binaryMessenger
564 codec:[FlutterJSONMethodCodec sharedInstance]];
565
566 if ([_initialRoute length] > 0) {
567 // Flutter isn't ready to receive this method call yet but the channel buffer will cache this.
568 [self.navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
569 _initialRoute = nil;
570 }
571
572 self.restorationChannel =
573 [[FlutterMethodChannel alloc] initWithName:@"flutter/restoration"
574 binaryMessenger:self.binaryMessenger
575 codec:[FlutterStandardMethodCodec sharedInstance]];
576
577 self.platformChannel =
578 [[FlutterMethodChannel alloc] initWithName:@"flutter/platform"
579 binaryMessenger:self.binaryMessenger
580 codec:[FlutterJSONMethodCodec sharedInstance]];
581
582 self.platformViewsChannel =
583 [[FlutterMethodChannel alloc] initWithName:@"flutter/platform_views"
584 binaryMessenger:self.binaryMessenger
585 codec:[FlutterStandardMethodCodec sharedInstance]];
586
587 self.textInputChannel =
588 [[FlutterMethodChannel alloc] initWithName:@"flutter/textinput"
589 binaryMessenger:self.binaryMessenger
590 codec:[FlutterJSONMethodCodec sharedInstance]];
591
592 self.undoManagerChannel =
593 [[FlutterMethodChannel alloc] initWithName:@"flutter/undomanager"
594 binaryMessenger:self.binaryMessenger
595 codec:[FlutterJSONMethodCodec sharedInstance]];
596
597 self.scribbleChannel =
598 [[FlutterMethodChannel alloc] initWithName:@"flutter/scribble"
599 binaryMessenger:self.binaryMessenger
600 codec:[FlutterJSONMethodCodec sharedInstance]];
601
602 self.spellCheckChannel =
603 [[FlutterMethodChannel alloc] initWithName:@"flutter/spellcheck"
604 binaryMessenger:self.binaryMessenger
605 codec:[FlutterStandardMethodCodec sharedInstance]];
606
607 self.lifecycleChannel =
608 [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/lifecycle"
609 binaryMessenger:self.binaryMessenger
610 codec:[FlutterStringCodec sharedInstance]];
611
612 self.systemChannel =
613 [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/system"
614 binaryMessenger:self.binaryMessenger
615 codec:[FlutterJSONMessageCodec sharedInstance]];
616
617 self.settingsChannel =
618 [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/settings"
619 binaryMessenger:self.binaryMessenger
620 codec:[FlutterJSONMessageCodec sharedInstance]];
621
622 self.keyEventChannel =
623 [[FlutterBasicMessageChannel alloc] initWithName:@"flutter/keyevent"
624 binaryMessenger:self.binaryMessenger
625 codec:[FlutterJSONMessageCodec sharedInstance]];
626
627 self.textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
628 self.textInputPlugin.indirectScribbleDelegate = self;
629 [self.textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
630
631 self.undoManagerPlugin = [[FlutterUndoManagerPlugin alloc] initWithDelegate:self];
632 self.platformPlugin = [[FlutterPlatformPlugin alloc] initWithEngine:self];
633
634 self.restorationPlugin =
635 [[FlutterRestorationPlugin alloc] initWithChannel:self.restorationChannel
636 restorationEnabled:self.restorationEnabled];
637 self.spellCheckPlugin = [[FlutterSpellCheckPlugin alloc] init];
638
639 self.screenshotChannel =
640 [[FlutterMethodChannel alloc] initWithName:@"flutter/screenshot"
641 binaryMessenger:self.binaryMessenger
642 codec:[FlutterStandardMethodCodec sharedInstance]];
643
644 [self.screenshotChannel setMethodCallHandler:^(FlutterMethodCall* _Nonnull call,
645 FlutterResult _Nonnull result) {
646 FlutterEngine* strongSelf = weakSelf;
647 if (!(strongSelf && strongSelf->_shell && strongSelf->_shell->IsSetup())) {
648 return result([FlutterError
649 errorWithCode:@"invalid_state"
650 message:@"Requesting screenshot while engine is not running."
651 details:nil]);
652 }
653 flutter::Rasterizer::Screenshot screenshot =
654 [strongSelf screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData base64Encode:NO];
655 if (!screenshot.data) {
656 return result([FlutterError errorWithCode:@"failure"
657 message:@"Unable to get screenshot."
658 details:nil]);
659 }
660 // TODO(gaaclarke): Find way to eliminate this data copy.
661 NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
662 length:screenshot.data->size()];
663 NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
664 NSNumber* width = @(screenshot.frame_size.width);
665 NSNumber* height = @(screenshot.frame_size.height);
666 return result(@[ width, height, format ?: [NSNull null], data ]);
667 }];
668 }
669
670 - (void)maybeSetupPlatformViewChannels {
671 if (_shell && self.shell.IsSetup()) {
672 __weak FlutterEngine* weakSelf = self;
673
674 [self.platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
675 [weakSelf.platformPlugin handleMethodCall:call result:result];
676 }];
677
678 [self.platformViewsChannel
679 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
680 if (weakSelf) {
681 [weakSelf.platformViewsController onMethodCall:call result:result];
682 }
683 }];
684
685 [self.textInputChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
686 [weakSelf.textInputPlugin handleMethodCall:call result:result];
687 }];
688
689 [self.undoManagerChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
690 [weakSelf.undoManagerPlugin handleMethodCall:call result:result];
691 }];
692
693 [self.spellCheckChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
694 [weakSelf.spellCheckPlugin handleMethodCall:call result:result];
695 }];
696 }
697 }
698
699 - (flutter::Rasterizer::Screenshot)screenshot:(flutter::Rasterizer::ScreenshotType)type
700 base64Encode:(bool)base64Encode {
701 return self.shell.Screenshot(type, base64Encode);
702 }
703
704 - (void)launchEngine:(NSString*)entrypoint
705 libraryURI:(NSString*)libraryOrNil
706 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
707 // Launch the Dart application with the inferred run configuration.
708 flutter::RunConfiguration configuration =
709 [self.dartProject runConfigurationForEntrypoint:entrypoint
710 libraryOrNil:libraryOrNil
711 entrypointArgs:entrypointArgs];
712
713 configuration.SetEngineId(self.engineIdentifier);
714 self.shell.RunEngine(std::move(configuration));
715 }
716
717 - (void)setUpShell:(std::unique_ptr<flutter::Shell>)shell
718 withVMServicePublication:(BOOL)doesVMServicePublication {
719 _shell = std::move(shell);
720 [self setUpChannels];
721 [self onLocaleUpdated:nil];
722 [self updateDisplays];
723 self.publisher = [[FlutterDartVMServicePublisher alloc]
724 initWithEnableVMServicePublication:doesVMServicePublication];
725 [self maybeSetupPlatformViewChannels];
726 _shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
727 : flutter::GpuAvailability::kAvailable);
728 }
729
730 + (BOOL)isProfilerEnabled {
731 bool profilerEnabled = false;
732 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
733 (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
734 profilerEnabled = true;
735 #endif
736 return profilerEnabled;
737 }
738
739 + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
740 static size_t s_shellCount = 0;
741 return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
742 }
743
744 static flutter::ThreadHost MakeThreadHost(NSString* thread_label,
745 const flutter::Settings& settings) {
746 // The current thread will be used as the platform thread. Ensure that the message loop is
747 // initialized.
748 fml::MessageLoop::EnsureInitializedForCurrentThread();
749
750 uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo;
751 if (settings.merged_platform_ui_thread != flutter::Settings::MergedPlatformUIThread::kEnabled) {
752 threadHostType |= flutter::ThreadHost::Type::kUi;
753 }
754
755 if ([FlutterEngine isProfilerEnabled]) {
756 threadHostType = threadHostType | flutter::ThreadHost::Type::kProfiler;
757 }
758
759 flutter::ThreadHost::ThreadHostConfig host_config(thread_label.UTF8String, threadHostType,
760 IOSPlatformThreadConfigSetter);
761
762 host_config.ui_config =
763 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
764 flutter::ThreadHost::Type::kUi, thread_label.UTF8String),
765 fml::Thread::ThreadPriority::kDisplay);
766 host_config.raster_config =
767 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
768 flutter::ThreadHost::Type::kRaster, thread_label.UTF8String),
769 fml::Thread::ThreadPriority::kRaster);
770
771 host_config.io_config =
772 fml::Thread::ThreadConfig(flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
773 flutter::ThreadHost::Type::kIo, thread_label.UTF8String),
774 fml::Thread::ThreadPriority::kNormal);
775
776 return (flutter::ThreadHost){host_config};
777 }
778
779 static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) {
780 if (libraryURI) {
781 FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library";
782 settings->advisory_script_entrypoint = entrypoint.UTF8String;
783 settings->advisory_script_uri = libraryURI.UTF8String;
784 } else if (entrypoint) {
785 settings->advisory_script_entrypoint = entrypoint.UTF8String;
786 settings->advisory_script_uri = std::string("main.dart");
787 } else {
788 settings->advisory_script_entrypoint = std::string("main");
789 settings->advisory_script_uri = std::string("main.dart");
790 }
791 }
792
793 - (BOOL)createShell:(NSString*)entrypoint
794 libraryURI:(NSString*)libraryURI
795 initialRoute:(NSString*)initialRoute {
796 if (_shell != nullptr) {
797 [FlutterLogger logWarning:@"This FlutterEngine was already invoked."];
798 return NO;
799 }
800
801 self.initialRoute = initialRoute;
802
803 auto settings = [self.dartProject settings];
804 if (initialRoute != nil) {
805 self.initialRoute = initialRoute;
806 } else if (settings.route.empty() == false) {
807 self.initialRoute = [NSString stringWithUTF8String:settings.route.c_str()];
808 }
809
810 auto platformData = [self.dartProject defaultPlatformData];
811
812 SetEntryPoint(&settings, entrypoint, libraryURI);
813
814 NSString* threadLabel = [FlutterEngine generateThreadLabel:self.labelPrefix];
815 _threadHost = std::make_shared<flutter::ThreadHost>();
816 *_threadHost = MakeThreadHost(threadLabel, settings);
817
818 __weak FlutterEngine* weakSelf = self;
819 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
820 [weakSelf](flutter::Shell& shell) {
821 FlutterEngine* strongSelf = weakSelf;
822 if (!strongSelf) {
823 return std::unique_ptr<flutter::PlatformViewIOS>();
824 }
825 [strongSelf recreatePlatformViewsController];
826 strongSelf.platformViewsController.taskRunner =
827 shell.GetTaskRunners().GetPlatformTaskRunner();
828 return std::make_unique<flutter::PlatformViewIOS>(
829 shell, strongSelf->_renderingApi, strongSelf.platformViewsController,
830 shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
831 shell.GetIsGpuDisabledSyncSwitch());
832 };
833
834 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
835 [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
836
837 fml::RefPtr<fml::TaskRunner> ui_runner;
838 if (settings.enable_impeller &&
839 settings.merged_platform_ui_thread == flutter::Settings::MergedPlatformUIThread::kEnabled) {
840 ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
841 } else {
842 ui_runner = _threadHost->ui_thread->GetTaskRunner();
843 }
844 flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
845 fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
846 _threadHost->raster_thread->GetTaskRunner(), // raster
847 ui_runner, // ui
848 _threadHost->io_thread->GetTaskRunner() // io
849 );
850
851 // Disable GPU if the app or scene is running in the background.
852 self.isGpuDisabled = self.viewController
853 ? self.viewController.stateIsBackground
854 : FlutterSharedApplication.application &&
855 FlutterSharedApplication.application.applicationState ==
856 UIApplicationStateBackground;
857
858 // Create the shell. This is a blocking operation.
859 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
860 /*platform_data=*/platformData,
861 /*task_runners=*/task_runners,
862 /*settings=*/settings,
863 /*on_create_platform_view=*/on_create_platform_view,
864 /*on_create_rasterizer=*/on_create_rasterizer,
865 /*is_gpu_disabled=*/_isGpuDisabled);
866
867 if (shell == nullptr) {
868 NSString* errorMessage = [NSString
869 stringWithFormat:@"Could not start a shell FlutterEngine with entrypoint: %@", entrypoint];
870 [FlutterLogger logError:errorMessage];
871 } else {
872 [self setUpShell:std::move(shell)
873 withVMServicePublication:settings.enable_vm_service_publication];
874 if ([FlutterEngine isProfilerEnabled]) {
875 [self startProfiler];
876 }
877 }
878
879 return _shell != nullptr;
880 }
881
882 - (void)updateDisplays {
883 if (!_shell) {
884 // Tests may do this.
885 return;
886 }
887 auto vsync_waiter = _shell->GetVsyncWaiter().lock();
888 auto vsync_waiter_ios = std::static_pointer_cast<flutter::VsyncWaiterIOS>(vsync_waiter);
889 std::vector<std::unique_ptr<flutter::Display>> displays;
890 auto screen_size = UIScreen.mainScreen.nativeBounds.size;
891 auto scale = UIScreen.mainScreen.scale;
892 displays.push_back(std::make_unique<flutter::VariableRefreshRateDisplay>(
893 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale));
894 _shell->OnDisplayUpdates(std::move(displays));
895 }
896
897 - (BOOL)run {
898 return [self runWithEntrypoint:FlutterDefaultDartEntrypoint
899 libraryURI:nil
900 initialRoute:FlutterDefaultInitialRoute];
901 }
902
903 - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
904 return [self runWithEntrypoint:entrypoint
905 libraryURI:libraryURI
906 initialRoute:FlutterDefaultInitialRoute];
907 }
908
909 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
910 return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:FlutterDefaultInitialRoute];
911 }
912
913 - (BOOL)runWithEntrypoint:(NSString*)entrypoint initialRoute:(NSString*)initialRoute {
914 return [self runWithEntrypoint:entrypoint libraryURI:nil initialRoute:initialRoute];
915 }
916
917 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
918 libraryURI:(NSString*)libraryURI
919 initialRoute:(NSString*)initialRoute {
920 return [self runWithEntrypoint:entrypoint
921 libraryURI:libraryURI
922 initialRoute:initialRoute
923 entrypointArgs:nil];
924 }
925
926 - (BOOL)runWithEntrypoint:(NSString*)entrypoint
927 libraryURI:(NSString*)libraryURI
928 initialRoute:(NSString*)initialRoute
929 entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
930 if ([self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
931 [self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
932 }
933
934 return _shell != nullptr;
935 }
936
937 - (void)notifyLowMemory {
938 if (_shell) {
939 _shell->NotifyLowMemoryWarning();
940 }
941 [self.systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
942 }
943
944 #pragma mark - Text input delegate
945
946 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
947 updateEditingClient:(int)client
948 withState:(NSDictionary*)state {
949 [self.textInputChannel invokeMethod:@"TextInputClient.updateEditingState"
950 arguments:@[ @(client), state ]];
951 }
952
953 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
954 updateEditingClient:(int)client
955 withState:(NSDictionary*)state
956 withTag:(NSString*)tag {
957 [self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithTag"
958 arguments:@[ @(client), @{tag : state} ]];
959 }
960
961 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
962 updateEditingClient:(int)client
963 withDelta:(NSDictionary*)delta {
964 [self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
965 arguments:@[ @(client), delta ]];
966 }
967
968 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
969 updateFloatingCursor:(FlutterFloatingCursorDragState)state
970 withClient:(int)client
971 withPosition:(NSDictionary*)position {
972 NSString* stateString;
973 switch (state) {
974 case FlutterFloatingCursorDragStateStart:
975 stateString = @"FloatingCursorDragState.start";
976 break;
977 case FlutterFloatingCursorDragStateUpdate:
978 stateString = @"FloatingCursorDragState.update";
979 break;
980 case FlutterFloatingCursorDragStateEnd:
981 stateString = @"FloatingCursorDragState.end";
982 break;
983 }
984 [self.textInputChannel invokeMethod:@"TextInputClient.updateFloatingCursor"
985 arguments:@[ @(client), stateString, position ]];
986 }
987
988 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
989 performAction:(FlutterTextInputAction)action
990 withClient:(int)client {
991 NSString* actionString;
992 switch (action) {
993 case FlutterTextInputActionUnspecified:
994 // Where did the term "unspecified" come from? iOS has a "default" and Android
995 // has "unspecified." These 2 terms seem to mean the same thing but we need
996 // to pick just one. "unspecified" was chosen because "default" is often a
997 // reserved word in languages with switch statements (dart, java, etc).
998 actionString = @"TextInputAction.unspecified";
999 break;
1000 case FlutterTextInputActionDone:
1001 actionString = @"TextInputAction.done";
1002 break;
1003 case FlutterTextInputActionGo:
1004 actionString = @"TextInputAction.go";
1005 break;
1006 case FlutterTextInputActionSend:
1007 actionString = @"TextInputAction.send";
1008 break;
1009 case FlutterTextInputActionSearch:
1010 actionString = @"TextInputAction.search";
1011 break;
1012 case FlutterTextInputActionNext:
1013 actionString = @"TextInputAction.next";
1014 break;
1015 case FlutterTextInputActionContinue:
1016 actionString = @"TextInputAction.continueAction";
1017 break;
1018 case FlutterTextInputActionJoin:
1019 actionString = @"TextInputAction.join";
1020 break;
1021 case FlutterTextInputActionRoute:
1022 actionString = @"TextInputAction.route";
1023 break;
1024 case FlutterTextInputActionEmergencyCall:
1025 actionString = @"TextInputAction.emergencyCall";
1026 break;
1027 case FlutterTextInputActionNewline:
1028 actionString = @"TextInputAction.newline";
1029 break;
1030 }
1031 [self.textInputChannel invokeMethod:@"TextInputClient.performAction"
1032 arguments:@[ @(client), actionString ]];
1033 }
1034
1035 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1036 showAutocorrectionPromptRectForStart:(NSUInteger)start
1037 end:(NSUInteger)end
1038 withClient:(int)client {
1039 [self.textInputChannel invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
1040 arguments:@[ @(client), @(start), @(end) ]];
1041 }
1042
1043 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1044 willDismissEditMenuWithTextInputClient:(int)client {
1045 [self.platformChannel invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
1046 arguments:@[ @(client) ]];
1047 }
1048
1049 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1050 shareSelectedText:(NSString*)selectedText {
1051 [self.platformPlugin showShareViewController:selectedText];
1052 }
1053
1054 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1055 searchWebWithSelectedText:(NSString*)selectedText {
1056 [self.platformPlugin searchWeb:selectedText];
1057 }
1058
1059 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1060 lookUpSelectedText:(NSString*)selectedText {
1061 [self.platformPlugin showLookUpViewController:selectedText];
1062 }
1063
1064 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1065 performContextMenuCustomActionWithActionID:(NSString*)actionID
1066 textInputClient:(int)client {
1067 [self.platformChannel invokeMethod:@"ContextMenu.onPerformCustomAction"
1068 arguments:@[ @(client), actionID ]];
1069 }
1070
1071 #pragma mark - FlutterViewEngineDelegate
1072
1073 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView showToolbar:(int)client {
1074 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1075 // the framework has finished transitioning to the Scribble channel.
1076 // https://github.com/flutter/flutter/pull/115296
1077 [self.textInputChannel invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
1078 }
1079
1080 - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
1081 focusElement:(UIScribbleElementIdentifier)elementIdentifier
1082 atPoint:(CGPoint)referencePoint
1083 result:(FlutterResult)callback {
1084 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1085 // the framework has finished transitioning to the Scribble channel.
1086 // https://github.com/flutter/flutter/pull/115296
1087 [self.textInputChannel
1088 invokeMethod:@"TextInputClient.focusElement"
1089 arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
1090 result:callback];
1091 }
1092
1093 - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
1094 requestElementsInRect:(CGRect)rect
1095 result:(FlutterResult)callback {
1096 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1097 // the framework has finished transitioning to the Scribble channel.
1098 // https://github.com/flutter/flutter/pull/115296
1099 [self.textInputChannel
1100 invokeMethod:@"TextInputClient.requestElementsInRect"
1101 arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
1102 result:callback];
1103 }
1104
1105 - (void)flutterTextInputViewScribbleInteractionBegan:(FlutterTextInputView*)textInputView {
1106 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1107 // the framework has finished transitioning to the Scribble channel.
1108 // https://github.com/flutter/flutter/pull/115296
1109 [self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
1110 }
1111
1112 - (void)flutterTextInputViewScribbleInteractionFinished:(FlutterTextInputView*)textInputView {
1113 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1114 // the framework has finished transitioning to the Scribble channel.
1115 // https://github.com/flutter/flutter/pull/115296
1116 [self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionFinished" arguments:nil];
1117 }
1118
1119 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1120 insertTextPlaceholderWithSize:(CGSize)size
1121 withClient:(int)client {
1122 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1123 // the framework has finished transitioning to the Scribble channel.
1124 // https://github.com/flutter/flutter/pull/115296
1125 [self.textInputChannel invokeMethod:@"TextInputClient.insertTextPlaceholder"
1126 arguments:@[ @(client), @(size.width), @(size.height) ]];
1127 }
1128
1129 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1130 removeTextPlaceholder:(int)client {
1131 // TODO(justinmc): Switch from the TextInputClient to Scribble channel when
1132 // the framework has finished transitioning to the Scribble channel.
1133 // https://github.com/flutter/flutter/pull/115296
1134 [self.textInputChannel invokeMethod:@"TextInputClient.removeTextPlaceholder"
1135 arguments:@[ @(client) ]];
1136 }
1137
1138 - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
1139 didResignFirstResponderWithTextInputClient:(int)client {
1140 // When flutter text input view resign first responder, send a message to
1141 // framework to ensure the focus state is correct. This is useful when close
1142 // keyboard from platform side.
1143 [self.textInputChannel invokeMethod:@"TextInputClient.onConnectionClosed"
1144 arguments:@[ @(client) ]];
1145
1146 // Platform view's first responder detection logic:
1147 //
1148 // All text input widgets (e.g. EditableText) are backed by a dummy UITextInput view
1149 // in the TextInputPlugin. When this dummy UITextInput view resigns first responder,
1150 // check if any platform view becomes first responder. If any platform view becomes
1151 // first responder, send a "viewFocused" channel message to inform the framework to un-focus
1152 // the previously focused text input.
1153 //
1154 // Caveat:
1155 // 1. This detection logic does not cover the scenario when a platform view becomes
1156 // first responder without any flutter text input resigning its first responder status
1157 // (e.g. user tapping on platform view first). For now it works fine because the TextInputPlugin
1158 // does not track the focused platform view id (which is different from Android implementation).
1159 //
1160 // 2. This detection logic assumes that all text input widgets are backed by a dummy
1161 // UITextInput view in the TextInputPlugin, which may not hold true in the future.
1162
1163 // Have to check in the next run loop, because iOS requests the previous first responder to
1164 // resign before requesting the next view to become first responder.
1165 dispatch_async(dispatch_get_main_queue(), ^(void) {
1166 long platform_view_id = [self.platformViewsController firstResponderPlatformViewId];
1167 if (platform_view_id == -1) {
1168 return;
1169 }
1170
1171 [self.platformViewsChannel invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
1172 });
1173 }
1174
1175 #pragma mark - Undo Manager Delegate
1176
1177 - (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1178 NSString* action = (direction == FlutterUndoRedoDirectionUndo) ? @"undo" : @"redo";
1179 [self.undoManagerChannel invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
1180 }
1181
1182 - (UIView<UITextInput>*)activeTextInputView {
1183 return [[self textInputPlugin] textInputView];
1184 }
1185
1186 - (NSUndoManager*)undoManager {
1187 return self.viewController.undoManager;
1188 }
1189
1190 #pragma mark - Screenshot Delegate
1191
1192 - (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type
1193 asBase64Encoded:(BOOL)base64Encode {
1194 FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell";
1195 return _shell->Screenshot(type, base64Encode);
1196 }
1197
1198 - (void)flutterViewAccessibilityDidCall {
1199 if (self.viewController.view.accessibilityElements == nil) {
1200 [self ensureSemanticsEnabled];
1201 }
1202 }
1203
1204 - (NSObject<FlutterBinaryMessenger>*)binaryMessenger {
1205 return _binaryMessenger;
1206 }
1207
1208 - (NSObject<FlutterTextureRegistry>*)textureRegistry {
1209 return _textureRegistry;
1210 }
1211
1212 // For test only. Ideally we should create a dependency injector for all dependencies and
1213 // remove this.
1214 - (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger {
1215 // Discard the previous messenger and keep the new one.
1216 if (binaryMessenger != _binaryMessenger) {
1217 _binaryMessenger.parent = nil;
1218 _binaryMessenger = binaryMessenger;
1219 }
1220 }
1221
1222 #pragma mark - FlutterBinaryMessenger
1223
1224 - (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
1225 [self sendOnChannel:channel message:message binaryReply:nil];
1226 }
1227
1228 - (void)sendOnChannel:(NSString*)channel
1229 message:(NSData*)message
1230 binaryReply:(FlutterBinaryReply)callback {
1231 NSParameterAssert(channel);
1232 NSAssert(_shell && _shell->IsSetup(),
1233 @"Sending a message before the FlutterEngine has been run.");
1234 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
1235 (callback == nil) ? nullptr
1236 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
1237 ^(NSData* reply) {
1238 callback(reply);
1239 },
1240 _shell->GetTaskRunners().GetPlatformTaskRunner());
1241 std::unique_ptr<flutter::PlatformMessage> platformMessage =
1242 (message == nil) ? std::make_unique<flutter::PlatformMessage>(channel.UTF8String, response)
1243 : std::make_unique<flutter::PlatformMessage>(
1244 channel.UTF8String, flutter::CopyNSDataToMapping(message), response);
1245
1246 _shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
1247 // platformMessage takes ownership of response.
1248 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1249 }
1250
1251 - (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue {
1252 return flutter::PlatformMessageHandlerIos::MakeBackgroundTaskQueue();
1253 }
1254
1255 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
1256 binaryMessageHandler:
1257 (FlutterBinaryMessageHandler)handler {
1258 return [self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
1259 }
1260
1261 - (FlutterBinaryMessengerConnection)
1262 setMessageHandlerOnChannel:(NSString*)channel
1263 binaryMessageHandler:(FlutterBinaryMessageHandler)handler
1264 taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
1265 NSParameterAssert(channel);
1266 if (_shell && _shell->IsSetup()) {
1267 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
1268 handler, taskQueue);
1269 return [self.connections acquireConnectionForChannel:channel];
1270 } else {
1271 NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
1272 // Setting a handler to nil for a channel that has not yet been set up is a no-op.
1273 return [FlutterConnectionCollection makeErrorConnectionWithErrorCode:-1L];
1274 }
1275 }
1276
1277 - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1278 if (_shell && _shell->IsSetup()) {
1279 NSString* channel = [self.connections cleanupConnectionWithID:connection];
1280 if (channel.length > 0) {
1281 self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String, nil,
1282 nil);
1283 }
1284 }
1285 }
1286
1287 #pragma mark - FlutterTextureRegistry
1288
1289 - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
1290 FML_DCHECK(self.platformView);
1291 int64_t textureId = self.nextTextureId++;
1292 self.platformView->RegisterExternalTexture(textureId, texture);
1293 return textureId;
1294 }
1295
1296 - (void)unregisterTexture:(int64_t)textureId {
1297 _shell->GetPlatformView()->UnregisterTexture(textureId);
1298 }
1299
1300 - (void)textureFrameAvailable:(int64_t)textureId {
1301 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId);
1302 }
1303
1304 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1305 return [FlutterDartProject lookupKeyForAsset:asset];
1306 }
1307
1308 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1309 return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
1310 }
1311
1312 - (id<FlutterPluginRegistry>)pluginRegistry {
1313 return self;
1314 }
1315
1316 #pragma mark - FlutterPluginRegistry
1317
1318 - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
1319 NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
1320 self.pluginPublications[pluginKey] = [NSNull null];
1321 FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
1322 flutterEngine:self];
1323 self.registrars[pluginKey] = result;
1324 return result;
1325 }
1326
1327 - (BOOL)hasPlugin:(NSString*)pluginKey {
1328 return _pluginPublications[pluginKey] != nil;
1329 }
1330
1331 - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
1332 return _pluginPublications[pluginKey];
1333 }
1334
1335 #pragma mark - Notifications
1336
1337 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1338 [self flutterWillEnterForeground:notification];
1339 }
1340
1341 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0)) {
1342 [self flutterDidEnterBackground:notification];
1343 }
1344
1345 - (void)applicationWillEnterForeground:(NSNotification*)notification {
1346 [self flutterWillEnterForeground:notification];
1347 }
1348
1349 - (void)applicationDidEnterBackground:(NSNotification*)notification {
1350 [self flutterDidEnterBackground:notification];
1351 }
1352
1353 - (void)flutterWillEnterForeground:(NSNotification*)notification {
1354 [self setIsGpuDisabled:NO];
1355 }
1356
1357 - (void)flutterDidEnterBackground:(NSNotification*)notification {
1358 [self setIsGpuDisabled:YES];
1359 [self notifyLowMemory];
1360 }
1361
1362 - (void)onMemoryWarning:(NSNotification*)notification {
1363 [self notifyLowMemory];
1364 }
1365
1366 - (void)setIsGpuDisabled:(BOOL)value {
1367 if (_shell) {
1368 _shell->SetGpuAvailability(value ? flutter::GpuAvailability::kUnavailable
1369 : flutter::GpuAvailability::kAvailable);
1370 }
1371 _isGpuDisabled = value;
1372 }
1373
1374 #pragma mark - Locale updates
1375
1376 - (void)onLocaleUpdated:(NSNotification*)notification {
1377 // Get and pass the user's preferred locale list to dart:ui.
1378 NSMutableArray<NSString*>* localeData = [[NSMutableArray alloc] init];
1379 NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
1380 for (NSString* localeID in preferredLocales) {
1381 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1382 NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
1383 NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
1384 NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
1385 NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
1386 if (!languageCode) {
1387 continue;
1388 }
1389 [localeData addObject:languageCode];
1390 [localeData addObject:(countryCode ? countryCode : @"")];
1391 [localeData addObject:(scriptCode ? scriptCode : @"")];
1392 [localeData addObject:(variantCode ? variantCode : @"")];
1393 }
1394 if (localeData.count == 0) {
1395 return;
1396 }
1397 [self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
1398 }
1399
1400 - (void)waitForFirstFrameSync:(NSTimeInterval)timeout
1401 callback:(NS_NOESCAPE void (^_Nonnull)(BOOL didTimeout))callback {
1402 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1403 fml::Status status = self.shell.WaitForFirstFrame(waitTime);
1404 callback(status.code() == fml::StatusCode::kDeadlineExceeded);
1405 }
1406
1407 - (void)waitForFirstFrame:(NSTimeInterval)timeout
1408 callback:(void (^_Nonnull)(BOOL didTimeout))callback {
1409 dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
1410 dispatch_group_t group = dispatch_group_create();
1411
1412 __weak FlutterEngine* weakSelf = self;
1413 __block BOOL didTimeout = NO;
1414 dispatch_group_async(group, queue, ^{
1415 FlutterEngine* strongSelf = weakSelf;
1416 if (!strongSelf) {
1417 return;
1418 }
1419
1420 fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
1421 fml::Status status = strongSelf.shell.WaitForFirstFrame(waitTime);
1422 didTimeout = status.code() == fml::StatusCode::kDeadlineExceeded;
1423 });
1424
1425 // Only execute the main queue task once the background task has completely finished executing.
1426 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
1427 // Strongly capture self on the task dispatched to the main thread.
1428 //
1429 // When we capture weakSelf strongly in the above block on a background thread, we risk the
1430 // possibility that all other strong references to FlutterEngine go out of scope while the block
1431 // executes and that the engine is dealloc'ed at the end of the above block on a background
1432 // thread. FlutterEngine is not safe to release on any thread other than the main thread.
1433 //
1434 // self is never nil here since it's a strong reference that's verified non-nil above, but we
1435 // use a conditional check to avoid an unused expression compiler warning.
1436 FlutterEngine* strongSelf = self;
1437 if (!strongSelf) {
1438 return;
1439 }
1440 callback(didTimeout);
1441 });
1442 }
1443
1444 - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint
1445 libraryURI:(/*nullable*/ NSString*)libraryURI
1446 initialRoute:(/*nullable*/ NSString*)initialRoute
1447 entrypointArgs:(/*nullable*/ NSArray<NSString*>*)entrypointArgs {
1448 NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run).");
1449 FlutterEngine* result = [[FlutterEngine alloc] initWithName:self.labelPrefix
1450 project:self.dartProject
1451 allowHeadlessExecution:self.allowHeadlessExecution];
1452 flutter::RunConfiguration configuration =
1453 [self.dartProject runConfigurationForEntrypoint:entrypoint
1454 libraryOrNil:libraryURI
1455 entrypointArgs:entrypointArgs];
1456
1457 configuration.SetEngineId(result.engineIdentifier);
1458
1459 fml::WeakPtr<flutter::PlatformView> platform_view = _shell->GetPlatformView();
1460 FML_DCHECK(platform_view);
1461 // Static-cast safe since this class always creates PlatformViewIOS instances.
1462 flutter::PlatformViewIOS* ios_platform_view =
1463 static_cast<flutter::PlatformViewIOS*>(platform_view.get());
1464 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->GetIosContext();
1465 FML_DCHECK(context);
1466
1467 // Lambda captures by pointers to ObjC objects are fine here because the
1468 // create call is synchronous.
1469 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
1470 [result, context](flutter::Shell& shell) {
1471 [result recreatePlatformViewsController];
1472 result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
1473 return std::make_unique<flutter::PlatformViewIOS>(
1474 shell, context, result.platformViewsController, shell.GetTaskRunners());
1475 };
1476
1477 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
1478 [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
1479
1480 std::string cppInitialRoute;
1481 if (initialRoute) {
1482 cppInitialRoute = [initialRoute UTF8String];
1483 }
1484
1485 std::unique_ptr<flutter::Shell> shell = _shell->Spawn(
1486 std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
1487
1488 result->_threadHost = _threadHost;
1489 result->_profiler = _profiler;
1490 result->_isGpuDisabled = _isGpuDisabled;
1491 [result setUpShell:std::move(shell) withVMServicePublication:NO];
1492 return result;
1493 }
1494
1495 - (const flutter::ThreadHost&)threadHost {
1496 return *_threadHost;
1497 }
1498
1499 - (FlutterDartProject*)project {
1500 return self.dartProject;
1501 }
1502
1503 @end
1504
1505 @implementation FlutterEngineRegistrar {
1506 NSString* _pluginKey;
1507 }
1508
1509 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
1510 self = [super init];
1511 NSAssert(self, @"Super init cannot be nil");
1512 _pluginKey = [pluginKey copy];
1513 _flutterEngine = flutterEngine;
1514 return self;
1515 }
1516
1517 - (NSObject<FlutterBinaryMessenger>*)messenger {
1518 return _flutterEngine.binaryMessenger;
1519 }
1520
1521 - (NSObject<FlutterTextureRegistry>*)textures {
1522 return _flutterEngine.textureRegistry;
1523 }
1524
1525 - (nullable UIViewController*)viewController {
1526 return _flutterEngine.viewController;
1527 }
1528
1529 - (void)publish:(NSObject*)value {
1530 _flutterEngine.pluginPublications[_pluginKey] = value;
1531 }
1532
1533 - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
1534 channel:(FlutterMethodChannel*)channel {
1535 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1536 [delegate handleMethodCall:call result:result];
1537 }];
1538 }
1539
1540 - (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate
1541 NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions") {
1542 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
1543 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
1544 id<FlutterAppLifeCycleProvider> lifeCycleProvider =
1545 (id<FlutterAppLifeCycleProvider>)appDelegate;
1546 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
1547 }
1548 }
1549
1550 - (NSString*)lookupKeyForAsset:(NSString*)asset {
1551 return [_flutterEngine lookupKeyForAsset:asset];
1552 }
1553
1554 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
1555 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package];
1556 }
1557
1558 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1559 withId:(NSString*)factoryId {
1560 [self registerViewFactory:factory
1561 withId:factoryId
1562 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1563 }
1564
1565 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
1566 withId:(NSString*)factoryId
1567 gestureRecognizersBlockingPolicy:
1568 (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
1569 [_flutterEngine.platformViewsController registerViewFactory:factory
1570 withId:factoryId
1571 gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];
1572 }
1573
1574 @end
FlutterBinaryReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
FlutterBinaryMessageHandler
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
Definition: FlutterBinaryMessenger.h:30
FlutterBinaryMessengerConnection
int64_t FlutterBinaryMessengerConnection
Definition: FlutterBinaryMessenger.h:32
FlutterBinaryMessengerRelay.h
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:194
FlutterDartProject_Internal.h
FlutterDartVMServicePublisher.h
FlutterDefaultDartEntrypoint
NSString *const FlutterDefaultDartEntrypoint
Definition: FlutterEngine.mm:85
_profiler
std::shared_ptr< flutter::SamplingProfiler > _profiler
Definition: FlutterEngine.mm:160
_shell
std::unique_ptr< flutter::Shell > _shell
Definition: FlutterEngine.mm:155
kFlutterKeyDataChannel
NSString *const kFlutterKeyDataChannel
Definition: FlutterEngine.mm:90
FlutterDefaultInitialRoute
NSString *const FlutterDefaultInitialRoute
Definition: FlutterEngine.mm:86
_renderingApi
flutter::IOSRenderingAPI _renderingApi
Definition: FlutterEngine.mm:159
_textureRegistry
FlutterTextureRegistryRelay * _textureRegistry
Definition: FlutterEngine.mm:163
IOSPlatformThreadConfigSetter
static FLUTTER_ASSERT_ARC void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig &config)
Definition: FlutterEngine.mm:51
kNumProfilerSamplesPerSec
static constexpr int kNumProfilerSamplesPerSec
Definition: FlutterEngine.mm:91
_binaryMessenger
FlutterBinaryMessengerRelay * _binaryMessenger
Definition: FlutterEngine.mm:162
FlutterEngine_Internal.h
platform_view
std::unique_ptr< flutter::PlatformViewIOS > platform_view
Definition: FlutterEnginePlatformViewTest.mm:68
FlutterIndirectScribbleDelegate.h
FlutterPlatformPlugin.h
FlutterPlatformViewGestureRecognizersBlockingPolicy
FlutterPlatformViewGestureRecognizersBlockingPolicy
Definition: FlutterPlugin.h:252
_restorationEnabled
BOOL _restorationEnabled
Definition: FlutterRestorationPlugin.mm:18
FlutterSharedApplication.h
FlutterSpellCheckPlugin.h
FlutterTextInputDelegate.h
viewController
FlutterViewController * viewController
Definition: FlutterTextInputPluginTest.mm:139
textInputPlugin
FlutterTextInputPlugin * textInputPlugin
Definition: FlutterTextInputPluginTest.mm:137
FlutterTextureRegistryRelay.h
FlutterUndoManagerDelegate.h
FlutterUndoManagerPlugin.h
FlutterViewController_Internal.h
UIViewController+FlutterScreenAndSceneIfLoaded.h
FlutterEngine(Test)::enableEmbedderAPI
BOOL enableEmbedderAPI
Definition: FlutterEngine_Test.h:25
FlutterEngine(Test)::embedderAPI
FlutterEngineProcTable & embedderAPI
Definition: FlutterEngine_Test.h:24
FlutterDartProject
Definition: FlutterDartProject.mm:268
+[FlutterDartProject lookupKeyForAsset:fromPackage:]
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
Definition: FlutterDartProject.mm:403
-[FlutterDartProject settings]
const flutter::Settings & settings()
+[FlutterDartProject lookupKeyForAsset:]
NSString * lookupKeyForAsset:(NSString *asset)
Definition: FlutterDartProject.mm:394
FlutterTextInputView
Definition: FlutterTextInputPlugin.mm:812
flutter::PlatformMessageHandlerIos::MakeBackgroundTaskQueue
static NSObject< FlutterTaskQueue > * MakeBackgroundTaskQueue()
Definition: platform_message_handler_ios.mm:36
flutter::PlatformViewIOS
Definition: platform_view_ios.h:39
flutter::PlatformViewIOS::GetIosContext
const std::shared_ptr< IOSContext > & GetIosContext()
Definition: platform_view_ios.h:124
flutter::PlatformViewIOS::SetSemanticsEnabled
void SetSemanticsEnabled(bool enabled) override
Definition: platform_view_ios.mm:140
command_line.h
FlutterBasicMessageChannel
Definition: FlutterChannels.h:38
FlutterBinaryMessengerRelay
Definition: FlutterBinaryMessengerRelay.h:14
FlutterBinaryMessengerRelay::parent
NSObject< FlutterBinaryMessenger > * parent
Definition: FlutterBinaryMessengerRelay.h:15
FlutterDartVMServicePublisher
Definition: FlutterDartVMServicePublisher.h:11
FlutterEngine
Definition: FlutterEngine.h:61
FlutterEngine::textInputChannel
FlutterMethodChannel * textInputChannel
Definition: FlutterEngine.h:374
-[FlutterEngine platformView]
flutter::PlatformViewIOS * platformView()
-[FlutterEngine shell]
flutter::Shell & shell()
FlutterEngine::navigationChannel
FlutterMethodChannel * navigationChannel
Definition: FlutterEngine.h:345
FlutterEngine::keyEventChannel
FlutterBasicMessageChannel * keyEventChannel
Definition: FlutterEngine.h:412
FlutterEngine::lifecycleChannel
FlutterBasicMessageChannel * lifecycleChannel
Definition: FlutterEngine.h:385
FlutterEngine::platformChannel
FlutterMethodChannel * platformChannel
Definition: FlutterEngine.h:363
FlutterEngine::localizationChannel
FlutterMethodChannel * localizationChannel
Definition: FlutterEngine.h:335
FlutterEngine::isolateId
NSString * isolateId
Definition: FlutterEngine.h:439
FlutterEngine::systemChannel
FlutterBasicMessageChannel * systemChannel
Definition: FlutterEngine.h:396
FlutterEngine::settingsChannel
FlutterBasicMessageChannel * settingsChannel
Definition: FlutterEngine.h:404
FlutterEngine::restorationChannel
FlutterMethodChannel * restorationChannel
Definition: FlutterEngine.h:355
FlutterEngineRegistrar
Definition: FlutterEngine.mm:93
FlutterEngineRegistrar::flutterEngine
FlutterEngine * flutterEngine
Definition: FlutterEngine.mm:94
FlutterError
Definition: FlutterCodecs.h:247
+[FlutterError errorWithCode:message:details:]
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
FlutterJSONMessageCodec
Definition: FlutterCodecs.h:81
FlutterJSONMethodCodec
Definition: FlutterCodecs.h:455
FlutterMethodChannel
Definition: FlutterChannels.h:221
-[FlutterMethodChannel setMethodCallHandler:]
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
FlutterPlatformPlugin
Definition: FlutterPlatformPlugin.h:12
FlutterPlatformViewsController
Definition: FlutterPlatformViewsController.h:31
FlutterRestorationPlugin
Definition: FlutterRestorationPlugin.h:13
FlutterSharedApplication
Definition: FlutterSharedApplication.h:11
FlutterSharedApplication::application
UIApplication * application
Definition: FlutterSharedApplication.h:25
FlutterSharedApplication::isAvailable
BOOL isAvailable
Definition: FlutterSharedApplication.h:20
FlutterSpellCheckPlugin
Definition: FlutterSpellCheckPlugin.h:12
FlutterStandardMethodCodec
Definition: FlutterCodecs.h:469
FlutterStringCodec
Definition: FlutterCodecs.h:63
FlutterTextInputPlugin
Definition: FlutterTextInputPlugin.h:34
FlutterTexture
Definition: FlutterMetalLayer.mm:61
FlutterTextureRegistryRelay
Definition: FlutterTextureRegistryRelay.h:20
FlutterTextureRegistryRelay::parent
NSObject< FlutterTextureRegistry > * parent
Definition: FlutterTextureRegistryRelay.h:25
FlutterUndoManagerPlugin
Definition: FlutterUndoManagerPlugin.h:14
FlutterViewController
Definition: FlutterViewController.h:57
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
flutter
Definition: accessibility_bridge.h:26
flutter::CopyNSDataToMapping
fml::MallocMapping CopyNSDataToMapping(NSData *data)
Definition: buffer_conversions.mm:29
flutter::GetRenderingAPIForProcess
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
Definition: rendering_api_selection.mm:31
flutter::IOSRenderingAPI
IOSRenderingAPI
Definition: rendering_api_selection.h:14
fml
Definition: profiler_metrics_ios.mm:41
platform_message_response_darwin.h
platform_view_ios.h
profiler_metrics_ios.h
FlutterAppLifeCycleProvider-p
Definition: FlutterPlugin.h:471
FlutterBinaryMessenger-p
Definition: FlutterBinaryMessenger.h:49
FlutterIndirectScribbleDelegate-p
Definition: FlutterIndirectScribbleDelegate.h:13
+[FlutterMessageCodec-p sharedInstance]
instancetype sharedInstance()
FlutterPlatformViewFactory-p
Definition: FlutterPlatformViews.h:26
FlutterPlugin-p
Definition: FlutterPlugin.h:189
-[FlutterPlugin-p handleMethodCall:result:]
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
FlutterPluginRegistrar-p
Definition: FlutterPlugin.h:283
FlutterTaskQueue-p
Definition: FlutterBinaryMessenger.h:34
FlutterTextInputDelegate-p
Definition: FlutterTextInputDelegate.h:37
FlutterTextureRegistry-p
Definition: FlutterTexture.h:45
FlutterUndoManagerDelegate-p
Definition: FlutterUndoManagerDelegate.h:23
rendering_api_selection.h
vsync_waiter_ios.h