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