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