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