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