Flutter macOS Embedder
FlutterEngine.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
7 
8 #include <algorithm>
9 #include <iostream>
10 #include <vector>
11 
12 #include "flutter/common/constants.h"
15 #include "flutter/shell/platform/embedder/embedder.h"
16 
31 
33 
34 NSString* const kFlutterPlatformChannel = @"flutter/platform";
35 NSString* const kFlutterSettingsChannel = @"flutter/settings";
36 NSString* const kFlutterLifecycleChannel = @"flutter/lifecycle";
37 
38 using flutter::kFlutterImplicitViewId;
39 
40 /**
41  * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
42  * the returned struct.
43  */
44 static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
45  FlutterLocale flutterLocale = {};
46  flutterLocale.struct_size = sizeof(FlutterLocale);
47  flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
48  flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
49  flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
50  flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
51  return flutterLocale;
52 }
53 
54 /// The private notification for voice over.
55 static NSString* const kEnhancedUserInterfaceNotification =
56  @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
57 static NSString* const kEnhancedUserInterfaceKey = @"AXEnhancedUserInterface";
58 
59 /// Clipboard plain text format.
60 constexpr char kTextPlainFormat[] = "text/plain";
61 
62 #pragma mark -
63 
64 // Records an active handler of the messenger (FlutterEngine) that listens to
65 // platform messages on a given channel.
66 @interface FlutterEngineHandlerInfo : NSObject
67 
68 - (instancetype)initWithConnection:(NSNumber*)connection
69  handler:(FlutterBinaryMessageHandler)handler;
70 
71 @property(nonatomic, readonly) FlutterBinaryMessageHandler handler;
72 @property(nonatomic, readonly) NSNumber* connection;
73 
74 @end
75 
76 @implementation FlutterEngineHandlerInfo
77 - (instancetype)initWithConnection:(NSNumber*)connection
78  handler:(FlutterBinaryMessageHandler)handler {
79  self = [super init];
80  NSAssert(self, @"Super init cannot be nil");
82  _handler = handler;
83  return self;
84 }
85 @end
86 
87 #pragma mark -
88 
89 /**
90  * Private interface declaration for FlutterEngine.
91  */
93 
94 /**
95  * A mutable array that holds one bool value that determines if responses to platform messages are
96  * clear to execute. This value should be read or written only inside of a synchronized block and
97  * will return `NO` after the FlutterEngine has been dealloc'd.
98  */
99 @property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
100 
101 /**
102  * All delegates added via plugin calls to addApplicationDelegate.
103  */
104 @property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
105 
106 /**
107  * All registrars returned from registrarForPlugin:
108  */
109 @property(nonatomic, readonly)
110  NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
111 
112 - (nullable FlutterViewController*)viewControllerForIdentifier:
113  (FlutterViewIdentifier)viewIdentifier;
114 
115 /**
116  * An internal method that adds the view controller with the given ID.
117  *
118  * This method assigns the controller with the ID, puts the controller into the
119  * map, and does assertions related to the implicit view ID.
120  */
121 - (void)registerViewController:(FlutterViewController*)controller
122  forIdentifier:(FlutterViewIdentifier)viewIdentifier;
123 
124 /**
125  * An internal method that removes the view controller with the given ID.
126  *
127  * This method clears the ID of the controller, removes the controller from the
128  * map. This is an no-op if the view ID is not associated with any view
129  * controllers.
130  */
131 - (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier;
132 
133 /**
134  * Shuts down the engine if view requirement is not met, and headless execution
135  * is not allowed.
136  */
137 - (void)shutDownIfNeeded;
138 
139 /**
140  * Sends the list of user-preferred locales to the Flutter engine.
141  */
142 - (void)sendUserLocales;
143 
144 /**
145  * Handles a platform message from the engine.
146  */
147 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
148 
149 /**
150  * Invoked right before the engine is restarted.
151  *
152  * This should reset states to as if the application has just started. It
153  * usually indicates a hot restart (Shift-R in Flutter CLI.)
154  */
155 - (void)engineCallbackOnPreEngineRestart;
156 
157 /**
158  * Requests that the task be posted back the to the Flutter engine at the target time. The target
159  * time is in the clock used by the Flutter engine.
160  */
161 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
162 
163 /**
164  * Loads the AOT snapshots and instructions from the elf bundle (app_elf_snapshot.so) into _aotData,
165  * if it is present in the assets directory.
166  */
167 - (void)loadAOTData:(NSString*)assetsDir;
168 
169 /**
170  * Creates a platform view channel and sets up the method handler.
171  */
172 - (void)setUpPlatformViewChannel;
173 
174 /**
175  * Creates an accessibility channel and sets up the message handler.
176  */
177 - (void)setUpAccessibilityChannel;
178 
179 /**
180  * Handles messages received from the Flutter engine on the _*Channel channels.
181  */
182 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
183 
184 @end
185 
186 #pragma mark -
187 
189  __weak FlutterEngine* _engine;
191 }
192 
193 - (instancetype)initWithEngine:(FlutterEngine*)engine
194  terminator:(FlutterTerminationCallback)terminator {
195  self = [super init];
196  _acceptingRequests = NO;
197  _engine = engine;
198  _terminator = terminator ? terminator : ^(id sender) {
199  // Default to actually terminating the application. The terminator exists to
200  // allow tests to override it so that an actual exit doesn't occur.
201  [[NSApplication sharedApplication] terminate:sender];
202  };
203  id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
204  if ([appDelegate respondsToSelector:@selector(setTerminationHandler:)]) {
205  FlutterAppDelegate* flutterAppDelegate = reinterpret_cast<FlutterAppDelegate*>(appDelegate);
206  flutterAppDelegate.terminationHandler = self;
207  }
208  return self;
209 }
210 
211 // This is called by the method call handler in the engine when the application
212 // requests termination itself.
213 - (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*, id>*)arguments
214  result:(FlutterResult)result {
215  NSString* type = arguments[@"type"];
216  // Ignore the "exitCode" value in the arguments because AppKit doesn't have
217  // any good way to set the process exit code other than calling exit(), and
218  // that bypasses all of the native applicationShouldExit shutdown events,
219  // etc., which we don't want to skip.
220 
221  FlutterAppExitType exitType =
222  [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
223 
224  [self requestApplicationTermination:[NSApplication sharedApplication]
225  exitType:exitType
226  result:result];
227 }
228 
229 // This is called by the FlutterAppDelegate whenever any termination request is
230 // received.
231 - (void)requestApplicationTermination:(id)sender
232  exitType:(FlutterAppExitType)type
233  result:(nullable FlutterResult)result {
234  _shouldTerminate = YES;
235  if (![self acceptingRequests]) {
236  // Until the Dart application has signaled that it is ready to handle
237  // termination requests, the app will just terminate when asked.
238  type = kFlutterAppExitTypeRequired;
239  }
240  switch (type) {
241  case kFlutterAppExitTypeCancelable: {
242  FlutterJSONMethodCodec* codec = [FlutterJSONMethodCodec sharedInstance];
243  FlutterMethodCall* methodCall =
244  [FlutterMethodCall methodCallWithMethodName:@"System.requestAppExit" arguments:nil];
245  [_engine sendOnChannel:kFlutterPlatformChannel
246  message:[codec encodeMethodCall:methodCall]
247  binaryReply:^(NSData* _Nullable reply) {
248  NSAssert(_terminator, @"terminator shouldn't be nil");
249  id decoded_reply = [codec decodeEnvelope:reply];
250  if ([decoded_reply isKindOfClass:[FlutterError class]]) {
251  FlutterError* error = (FlutterError*)decoded_reply;
252  NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
253  [error details]);
254  _terminator(sender);
255  return;
256  }
257  if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
258  NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
259  decoded_reply);
260  _terminator(sender);
261  return;
262  }
263  NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
264  if ([replyArgs[@"response"] isEqual:@"exit"]) {
265  _terminator(sender);
266  } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
267  _shouldTerminate = NO;
268  }
269  if (result != nil) {
270  result(replyArgs);
271  }
272  }];
273  break;
274  }
275  case kFlutterAppExitTypeRequired:
276  NSAssert(_terminator, @"terminator shouldn't be nil");
277  _terminator(sender);
278  break;
279  }
280 }
281 
282 @end
283 
284 #pragma mark -
285 
286 @implementation FlutterPasteboard
287 
288 - (NSInteger)clearContents {
289  return [[NSPasteboard generalPasteboard] clearContents];
290 }
291 
292 - (NSString*)stringForType:(NSPasteboardType)dataType {
293  return [[NSPasteboard generalPasteboard] stringForType:dataType];
294 }
295 
296 - (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
297  return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
298 }
299 
300 @end
301 
302 #pragma mark -
303 
304 /**
305  * `FlutterPluginRegistrar` implementation handling a single plugin.
306  */
308 - (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
309  flutterEngine:(nonnull FlutterEngine*)flutterEngine;
310 
311 - (nullable NSView*)viewForIdentifier:(FlutterViewIdentifier)viewIdentifier;
312 
313 /**
314  * The value published by this plugin, or NSNull if nothing has been published.
315  *
316  * The unusual NSNull is for the documented behavior of valuePublishedByPlugin:.
317  */
318 @property(nonatomic, readonly, nonnull) NSObject* publishedValue;
319 @end
320 
321 @implementation FlutterEngineRegistrar {
322  NSString* _pluginKey;
324 }
325 
326 @dynamic view;
327 
328 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
329  self = [super init];
330  if (self) {
331  _pluginKey = [pluginKey copy];
332  _flutterEngine = flutterEngine;
333  _publishedValue = [NSNull null];
334  }
335  return self;
336 }
337 
338 #pragma mark - FlutterPluginRegistrar
339 
340 - (id<FlutterBinaryMessenger>)messenger {
342 }
343 
344 - (id<FlutterTextureRegistry>)textures {
345  return _flutterEngine.renderer;
346 }
347 
348 - (NSView*)view {
349  return [self viewForIdentifier:kFlutterImplicitViewId];
350 }
351 
352 - (NSView*)viewForIdentifier:(FlutterViewIdentifier)viewIdentifier {
353  FlutterViewController* controller = [_flutterEngine viewControllerForIdentifier:viewIdentifier];
354  if (controller == nil) {
355  return nil;
356  }
357  if (!controller.viewLoaded) {
358  [controller loadView];
359  }
360  return controller.flutterView;
361 }
362 
363 - (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
364  channel:(nonnull FlutterMethodChannel*)channel {
365  [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
366  [delegate handleMethodCall:call result:result];
367  }];
368 }
369 
370 - (void)addApplicationDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
371  id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
372  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
373  id<FlutterAppLifecycleProvider> lifeCycleProvider =
374  static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
375  [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
376  [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
377  }
378 }
379 
380 - (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
381  withId:(nonnull NSString*)factoryId {
382  [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
383 }
384 
385 - (void)publish:(NSObject*)value {
386  _publishedValue = value;
387 }
388 
389 - (NSString*)lookupKeyForAsset:(NSString*)asset {
390  return [FlutterDartProject lookupKeyForAsset:asset];
391 }
392 
393 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
394  return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
395 }
396 
397 @end
398 
399 // Callbacks provided to the engine. See the called methods for documentation.
400 #pragma mark - Static methods provided to engine configuration
401 
402 static void OnPlatformMessage(const FlutterPlatformMessage* message, void* user_data) {
403  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
404  [engine engineCallbackOnPlatformMessage:message];
405 }
406 
407 #pragma mark -
408 
409 @implementation FlutterEngine {
410  // The embedding-API-level engine object.
411  FLUTTER_API_SYMBOL(FlutterEngine) _engine;
412 
413  // The project being run by this engine.
415 
416  // A mapping of channel names to the registered information for those channels.
417  NSMutableDictionary<NSString*, FlutterEngineHandlerInfo*>* _messengerHandlers;
418 
419  // A self-incremental integer to assign to newly assigned channels as
420  // identification.
422 
423  // Whether the engine can continue running after the view controller is removed.
425 
426  // Pointer to the Dart AOT snapshot and instruction data.
427  _FlutterEngineAOTData* _aotData;
428 
429  // _macOSCompositor is created when the engine is created and its destruction is handled by ARC
430  // when the engine is destroyed.
431  std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;
432 
433  // The information of all views attached to this engine mapped from IDs.
434  //
435  // It can't use NSDictionary, because the values need to be weak references.
436  NSMapTable* _viewControllers;
437 
438  // FlutterCompositor is copied and used in embedder.cc.
439  FlutterCompositor _compositor;
440 
441  // Method channel for platform view functions. These functions include creating, disposing and
442  // mutating a platform view.
444 
445  // Used to support creation and deletion of platform views and registering platform view
446  // factories. Lifecycle is tied to the engine.
448 
449  // A message channel for sending user settings to the flutter engine.
451 
452  // A message channel for accessibility.
454 
455  // A method channel for miscellaneous platform functionality.
457 
459 
460  // Whether the application is currently the active application.
461  BOOL _active;
462 
463  // Whether any portion of the application is currently visible.
464  BOOL _visible;
465 
466  // Proxy to allow plugins, channels to hold a weak reference to the binary messenger (self).
468 
469  // Map from ViewId to vsync waiter. Note that this is modified on main thread
470  // but accessed on UI thread, so access must be @synchronized.
471  NSMapTable<NSNumber*, FlutterVSyncWaiter*>* _vsyncWaiters;
472 
473  // Weak reference to last view that received a pointer event. This is used to
474  // pair cursor change with a view.
476 }
477 
478 - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
479  return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
480 }
481 
482 static const int kMainThreadPriority = 47;
483 
484 static void SetThreadPriority(FlutterThreadPriority priority) {
485  if (priority == kDisplay || priority == kRaster) {
486  pthread_t thread = pthread_self();
487  sched_param param;
488  int policy;
489  if (!pthread_getschedparam(thread, &policy, &param)) {
490  param.sched_priority = kMainThreadPriority;
491  pthread_setschedparam(thread, policy, &param);
492  }
493  pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
494  }
495 }
496 
497 - (instancetype)initWithName:(NSString*)labelPrefix
498  project:(FlutterDartProject*)project
499  allowHeadlessExecution:(BOOL)allowHeadlessExecution {
500  self = [super init];
501  NSAssert(self, @"Super init cannot be nil");
502  _pasteboard = [[FlutterPasteboard alloc] init];
503  _active = NO;
504  _visible = NO;
505  _project = project ?: [[FlutterDartProject alloc] init];
506  _messengerHandlers = [[NSMutableDictionary alloc] init];
507  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
508  _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
509  _pluginRegistrars = [[NSMutableDictionary alloc] init];
511  _allowHeadlessExecution = allowHeadlessExecution;
512  _semanticsEnabled = NO;
513  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
514  _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
515  [_isResponseValid addObject:@YES];
516 
517  _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
518  FlutterEngineGetProcAddresses(&_embedderAPI);
519 
520  _viewControllers = [NSMapTable weakToWeakObjectsMapTable];
521  _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self];
522 
523  NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
524  [notificationCenter addObserver:self
525  selector:@selector(sendUserLocales)
526  name:NSCurrentLocaleDidChangeNotification
527  object:nil];
528 
531  // The macOS compositor must be initialized in the initializer because it is
532  // used when adding views, which might happen before runWithEntrypoint.
533  _macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
534  [[FlutterViewEngineProvider alloc] initWithEngine:self],
535  [[FlutterTimeConverter alloc] initWithEngine:self], _platformViewController);
536 
537  [self setUpPlatformViewChannel];
538  [self setUpAccessibilityChannel];
539  [self setUpNotificationCenterListeners];
540  id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
541  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
542  _terminationHandler = [[FlutterEngineTerminationHandler alloc] initWithEngine:self
543  terminator:nil];
544  id<FlutterAppLifecycleProvider> lifecycleProvider =
545  static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
546  [lifecycleProvider addApplicationLifecycleDelegate:self];
547  } else {
548  _terminationHandler = nil;
549  }
550 
551  _vsyncWaiters = [NSMapTable strongToStrongObjectsMapTable];
552 
553  return self;
554 }
555 
556 - (void)dealloc {
557  id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
558  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
559  id<FlutterAppLifecycleProvider> lifecycleProvider =
560  static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
561  [lifecycleProvider removeApplicationLifecycleDelegate:self];
562 
563  // Unregister any plugins that registered as app delegates, since they are not guaranteed to
564  // live after the engine is destroyed, and their delegation registration is intended to be bound
565  // to the engine and its lifetime.
566  for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
567  if (delegate) {
568  [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
569  }
570  }
571  }
572  // Clear any published values, just in case a plugin has created a retain cycle with the
573  // registrar.
574  for (NSString* pluginName in _pluginRegistrars) {
575  [_pluginRegistrars[pluginName] publish:[NSNull null]];
576  }
577  @synchronized(_isResponseValid) {
578  [_isResponseValid removeAllObjects];
579  [_isResponseValid addObject:@NO];
580  }
581  [self shutDownEngine];
582  if (_aotData) {
583  _embedderAPI.CollectAOTData(_aotData);
584  }
585 }
586 
587 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
588  if (self.running) {
589  return NO;
590  }
591 
592  if (!_allowHeadlessExecution && [_viewControllers count] == 0) {
593  NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
594  return NO;
595  }
596 
597  [self addInternalPlugins];
598 
599  // The first argument of argv is required to be the executable name.
600  std::vector<const char*> argv = {[self.executableName UTF8String]};
601  std::vector<std::string> switches = self.switches;
602 
603  // Enable Impeller only if specifically asked for from the project or cmdline arguments.
604  if (_project.enableImpeller ||
605  std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end()) {
606  switches.push_back("--enable-impeller=true");
607  }
608 
609  std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
610  [](const std::string& arg) -> const char* { return arg.c_str(); });
611 
612  std::vector<const char*> dartEntrypointArgs;
613  for (NSString* argument in [_project dartEntrypointArguments]) {
614  dartEntrypointArgs.push_back([argument UTF8String]);
615  }
616 
617  FlutterProjectArgs flutterArguments = {};
618  flutterArguments.struct_size = sizeof(FlutterProjectArgs);
619  flutterArguments.assets_path = _project.assetsPath.UTF8String;
620  flutterArguments.icu_data_path = _project.ICUDataPath.UTF8String;
621  flutterArguments.command_line_argc = static_cast<int>(argv.size());
622  flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
623  flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
624  flutterArguments.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
625  void* user_data) {
626  // TODO(dkwingsmt): This callback only supports single-view, therefore it
627  // only operates on the implicit view. To support multi-view, we need a
628  // way to pass in the ID (probably through FlutterSemanticsUpdate).
629  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
630  [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
631  };
632  flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
633  flutterArguments.shutdown_dart_vm_when_done = true;
634  flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
635  flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
636  flutterArguments.root_isolate_create_callback = _project.rootIsolateCreateCallback;
637  flutterArguments.log_message_callback = [](const char* tag, const char* message,
638  void* user_data) {
639  if (tag && tag[0]) {
640  std::cout << tag << ": ";
641  }
642  std::cout << message << std::endl;
643  };
644 
645  static size_t sTaskRunnerIdentifiers = 0;
646  const FlutterTaskRunnerDescription cocoa_task_runner_description = {
647  .struct_size = sizeof(FlutterTaskRunnerDescription),
648  // Retain for use in post_task_callback. Released in destruction_callback.
649  .user_data = (__bridge_retained void*)self,
650  .runs_task_on_current_thread_callback = [](void* user_data) -> bool {
651  return [[NSThread currentThread] isMainThread];
652  },
653  .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
654  void* user_data) -> void {
655  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
656  [engine postMainThreadTask:task targetTimeInNanoseconds:target_time_nanos];
657  },
658  .identifier = ++sTaskRunnerIdentifiers,
659  .destruction_callback =
660  [](void* user_data) {
661  // Balancing release for the retain when setting user_data above.
662  FlutterEngine* engine = (__bridge_transfer FlutterEngine*)user_data;
663  engine = nil;
664  },
665  };
666  const FlutterCustomTaskRunners custom_task_runners = {
667  .struct_size = sizeof(FlutterCustomTaskRunners),
668  .platform_task_runner = &cocoa_task_runner_description,
669  .thread_priority_setter = SetThreadPriority};
670  flutterArguments.custom_task_runners = &custom_task_runners;
671 
672  [self loadAOTData:_project.assetsPath];
673  if (_aotData) {
674  flutterArguments.aot_data = _aotData;
675  }
676 
677  flutterArguments.compositor = [self createFlutterCompositor];
678 
679  flutterArguments.on_pre_engine_restart_callback = [](void* user_data) {
680  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
681  [engine engineCallbackOnPreEngineRestart];
682  };
683 
684  flutterArguments.vsync_callback = [](void* user_data, intptr_t baton) {
685  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
686  [engine onVSync:baton];
687  };
688 
689  FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
690  FlutterEngineResult result = _embedderAPI.Initialize(
691  FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
692  if (result != kSuccess) {
693  NSLog(@"Failed to initialize Flutter engine: error %d", result);
694  return NO;
695  }
696 
697  result = _embedderAPI.RunInitialized(_engine);
698  if (result != kSuccess) {
699  NSLog(@"Failed to run an initialized engine: error %d", result);
700  return NO;
701  }
702 
703  [self sendUserLocales];
704 
705  // Update window metric for all view controllers.
706  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
707  FlutterViewController* nextViewController;
708  while ((nextViewController = [viewControllerEnumerator nextObject])) {
709  [self updateWindowMetricsForViewController:nextViewController];
710  }
711 
712  [self updateDisplayConfig];
713  // Send the initial user settings such as brightness and text scale factor
714  // to the engine.
715  [self sendInitialSettings];
716  return YES;
717 }
718 
719 - (void)loadAOTData:(NSString*)assetsDir {
720  if (!_embedderAPI.RunsAOTCompiledDartCode()) {
721  return;
722  }
723 
724  BOOL isDirOut = false; // required for NSFileManager fileExistsAtPath.
725  NSFileManager* fileManager = [NSFileManager defaultManager];
726 
727  // This is the location where the test fixture places the snapshot file.
728  // For applications built by Flutter tool, this is in "App.framework".
729  NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
730 
731  if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
732  return;
733  }
734 
735  FlutterEngineAOTDataSource source = {};
736  source.type = kFlutterEngineAOTDataSourceTypeElfPath;
737  source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
738 
739  auto result = _embedderAPI.CreateAOTData(&source, &_aotData);
740  if (result != kSuccess) {
741  NSLog(@"Failed to load AOT data from: %@", elfPath);
742  }
743 }
744 
745 - (void)registerViewController:(FlutterViewController*)controller
746  forIdentifier:(FlutterViewIdentifier)viewIdentifier {
747  _macOSCompositor->AddView(viewIdentifier);
748  NSAssert(controller != nil, @"The controller must not be nil.");
749  NSAssert(controller.engine == nil,
750  @"The FlutterViewController is unexpectedly attached to "
751  @"engine %@ before initialization.",
752  controller.engine);
753  NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil,
754  @"The requested view ID is occupied.");
755  [_viewControllers setObject:controller forKey:@(viewIdentifier)];
756  [controller setUpWithEngine:self
757  viewIdentifier:viewIdentifier
758  threadSynchronizer:_threadSynchronizer];
759  NSAssert(controller.viewIdentifier == viewIdentifier, @"Failed to assign view ID.");
760  // Verify that the controller's property are updated accordingly. Failing the
761  // assertions is likely because either the FlutterViewController or the
762  // FlutterEngine is mocked. Please subclass these classes instead.
763  NSAssert(controller.attached, @"The FlutterViewController should switch to the attached mode "
764  @"after it is added to a FlutterEngine.");
765  NSAssert(controller.engine == self,
766  @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
767  self, controller.engine);
768 
769  if (controller.viewLoaded) {
770  [self viewControllerViewDidLoad:controller];
771  }
772 }
773 
774 - (void)viewControllerViewDidLoad:(FlutterViewController*)viewController {
775  __weak FlutterEngine* weakSelf = self;
776  FlutterTimeConverter* timeConverter = [[FlutterTimeConverter alloc] initWithEngine:self];
777  FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
778  initWithDisplayLink:[FlutterDisplayLink displayLinkWithView:viewController.view]
779  block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
780  uintptr_t baton) {
781  uint64_t timeNanos = [timeConverter CAMediaTimeToEngineTime:timestamp];
782  uint64_t targetTimeNanos =
783  [timeConverter CAMediaTimeToEngineTime:targetTimestamp];
784  FlutterEngine* engine = weakSelf;
785  if (engine) {
786  // It is a bit unfortunate that embedder requires OnVSync call on
787  // platform thread just to immediately redispatch it to UI thread.
788  // We are already on UI thread right now, but have to do the
789  // extra hop to main thread.
790  [engine->_threadSynchronizer performOnPlatformThread:^{
791  engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
792  }];
793  }
794  }];
795  FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewIdentifier)] == nil);
796  @synchronized(_vsyncWaiters) {
797  [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
798  }
799 }
800 
801 - (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
802  _macOSCompositor->RemoveView(viewIdentifier);
803  FlutterViewController* controller = [self viewControllerForIdentifier:viewIdentifier];
804  // The controller can be nil. The engine stores only a weak ref, and this
805  // method could have been called from the controller's dealloc.
806  if (controller != nil) {
807  [controller detachFromEngine];
808  NSAssert(!controller.attached,
809  @"The FlutterViewController unexpectedly stays attached after being removed. "
810  @"In unit tests, this is likely because either the FlutterViewController or "
811  @"the FlutterEngine is mocked. Please subclass these classes instead.");
812  }
813  [_viewControllers removeObjectForKey:@(viewIdentifier)];
814  @synchronized(_vsyncWaiters) {
815  [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
816  }
817 }
818 
819 - (void)shutDownIfNeeded {
820  if ([_viewControllers count] == 0 && !_allowHeadlessExecution) {
821  [self shutDownEngine];
822  }
823 }
824 
825 - (FlutterViewController*)viewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
826  FlutterViewController* controller = [_viewControllers objectForKey:@(viewIdentifier)];
827  NSAssert(controller == nil || controller.viewIdentifier == viewIdentifier,
828  @"The stored controller has unexpected view ID.");
829  return controller;
830 }
831 
832 - (void)setViewController:(FlutterViewController*)controller {
833  FlutterViewController* currentController =
834  [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
835  if (currentController == controller) {
836  // From nil to nil, or from non-nil to the same controller.
837  return;
838  }
839  if (currentController == nil && controller != nil) {
840  // From nil to non-nil.
841  NSAssert(controller.engine == nil,
842  @"Failed to set view controller to the engine: "
843  @"The given FlutterViewController is already attached to an engine %@. "
844  @"If you wanted to create an FlutterViewController and set it to an existing engine, "
845  @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
846  controller.engine);
847  [self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
848  } else if (currentController != nil && controller == nil) {
849  NSAssert(currentController.viewIdentifier == kFlutterImplicitViewId,
850  @"The default controller has an unexpected ID %llu", currentController.viewIdentifier);
851  // From non-nil to nil.
852  [self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
853  [self shutDownIfNeeded];
854  } else {
855  // From non-nil to a different non-nil view controller.
856  NSAssert(NO,
857  @"Failed to set view controller to the engine: "
858  @"The engine already has an implicit view controller %@. "
859  @"If you wanted to make the implicit view render in a different window, "
860  @"you should attach the current view controller to the window instead.",
861  [_viewControllers objectForKey:@(kFlutterImplicitViewId)]);
862  }
863 }
864 
865 - (FlutterViewController*)viewController {
866  return [self viewControllerForIdentifier:kFlutterImplicitViewId];
867 }
868 
869 - (FlutterCompositor*)createFlutterCompositor {
870  _compositor = {};
871  _compositor.struct_size = sizeof(FlutterCompositor);
872  _compositor.user_data = _macOSCompositor.get();
873 
874  _compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, //
875  FlutterBackingStore* backing_store_out, //
876  void* user_data //
877  ) {
878  return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CreateBackingStore(
879  config, backing_store_out);
880  };
881 
882  _compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, //
883  void* user_data //
884  ) { return true; };
885 
886  _compositor.present_view_callback = [](const FlutterPresentViewInfo* info) {
887  return reinterpret_cast<flutter::FlutterCompositor*>(info->user_data)
888  ->Present(info->view_id, info->layers, info->layers_count);
889  };
890 
891  _compositor.avoid_backing_store_cache = true;
892 
893  return &_compositor;
894 }
895 
896 - (id<FlutterBinaryMessenger>)binaryMessenger {
897  return _binaryMessenger;
898 }
899 
900 #pragma mark - Framework-internal methods
901 
902 - (void)addViewController:(FlutterViewController*)controller {
903  // FlutterEngine can only handle the implicit view for now. Adding more views
904  // throws an assertion.
905  NSAssert(self.viewController == nil,
906  @"The engine already has a view controller for the implicit view.");
907  self.viewController = controller;
908 }
909 
910 - (void)removeViewController:(nonnull FlutterViewController*)viewController {
911  [self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
912  [self shutDownIfNeeded];
913 }
914 
915 - (BOOL)running {
916  return _engine != nullptr;
917 }
918 
919 - (void)updateDisplayConfig:(NSNotification*)notification {
920  [self updateDisplayConfig];
921 }
922 
923 - (NSArray<NSScreen*>*)screens {
924  return [NSScreen screens];
925 }
926 
927 - (void)updateDisplayConfig {
928  if (!_engine) {
929  return;
930  }
931 
932  std::vector<FlutterEngineDisplay> displays;
933  for (NSScreen* screen : [self screens]) {
934  CGDirectDisplayID displayID =
935  static_cast<CGDirectDisplayID>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
936 
937  double devicePixelRatio = screen.backingScaleFactor;
938  FlutterEngineDisplay display;
939  display.struct_size = sizeof(display);
940  display.display_id = displayID;
941  display.single_display = false;
942  display.width = static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
943  display.height = static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
944  display.device_pixel_ratio = devicePixelRatio;
945 
946  CVDisplayLinkRef displayLinkRef = nil;
947  CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
948 
949  if (error == 0) {
950  CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
951  if (!(nominal.flags & kCVTimeIsIndefinite)) {
952  double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
953  display.refresh_rate = round(refreshRate);
954  }
955  CVDisplayLinkRelease(displayLinkRef);
956  } else {
957  display.refresh_rate = 0;
958  }
959 
960  displays.push_back(display);
961  }
962  _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
963  displays.data(), displays.size());
964 }
965 
966 - (void)onSettingsChanged:(NSNotification*)notification {
967  // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
968  NSString* brightness =
969  [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
970  [_settingsChannel sendMessage:@{
971  @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
972  // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
973  @"textScaleFactor" : @1.0,
974  @"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]),
975  }];
976 }
977 
978 - (void)sendInitialSettings {
979  // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
980  [[NSDistributedNotificationCenter defaultCenter]
981  addObserver:self
982  selector:@selector(onSettingsChanged:)
983  name:@"AppleInterfaceThemeChangedNotification"
984  object:nil];
985  [self onSettingsChanged:nil];
986 }
987 
988 - (FlutterEngineProcTable&)embedderAPI {
989  return _embedderAPI;
990 }
991 
992 - (nonnull NSString*)executableName {
993  return [[[NSProcessInfo processInfo] arguments] firstObject] ?: @"Flutter";
994 }
995 
996 - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
997  if (!_engine || !viewController || !viewController.viewLoaded) {
998  return;
999  }
1000  NSAssert([self viewControllerForIdentifier:viewController.viewIdentifier] == viewController,
1001  @"The provided view controller is not attached to this engine.");
1002  NSView* view = viewController.flutterView;
1003  CGRect scaledBounds = [view convertRectToBacking:view.bounds];
1004  CGSize scaledSize = scaledBounds.size;
1005  double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
1006  auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
1007  const FlutterWindowMetricsEvent windowMetricsEvent = {
1008  .struct_size = sizeof(windowMetricsEvent),
1009  .width = static_cast<size_t>(scaledSize.width),
1010  .height = static_cast<size_t>(scaledSize.height),
1011  .pixel_ratio = pixelRatio,
1012  .left = static_cast<size_t>(scaledBounds.origin.x),
1013  .top = static_cast<size_t>(scaledBounds.origin.y),
1014  .display_id = static_cast<uint64_t>(displayId),
1015  .view_id = viewController.viewIdentifier,
1016  };
1017  _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1018 }
1019 
1020 - (void)sendPointerEvent:(const FlutterPointerEvent&)event {
1021  _embedderAPI.SendPointerEvent(_engine, &event, 1);
1022  _lastViewWithPointerEvent = [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView;
1023 }
1024 
1025 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
1026  callback:(FlutterKeyEventCallback)callback
1027  userData:(void*)userData {
1028  _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
1029 }
1030 
1031 - (void)setSemanticsEnabled:(BOOL)enabled {
1032  if (_semanticsEnabled == enabled) {
1033  return;
1034  }
1035  _semanticsEnabled = enabled;
1036 
1037  // Update all view controllers' bridges.
1038  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1039  FlutterViewController* nextViewController;
1040  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1041  [nextViewController notifySemanticsEnabledChanged];
1042  }
1043 
1044  _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1045 }
1046 
1047 - (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1048  toTarget:(uint16_t)target
1049  withData:(fml::MallocMapping)data {
1050  _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1051 }
1052 
1053 - (FlutterPlatformViewController*)platformViewController {
1054  return _platformViewController;
1055 }
1056 
1057 #pragma mark - Private methods
1058 
1059 - (void)sendUserLocales {
1060  if (!self.running) {
1061  return;
1062  }
1063 
1064  // Create a list of FlutterLocales corresponding to the preferred languages.
1065  NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1066  std::vector<FlutterLocale> flutterLocales;
1067  flutterLocales.reserve(locales.count);
1068  for (NSString* localeID in [NSLocale preferredLanguages]) {
1069  NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1070  [locales addObject:locale];
1071  flutterLocales.push_back(FlutterLocaleFromNSLocale(locale));
1072  }
1073  // Convert to a list of pointers, and send to the engine.
1074  std::vector<const FlutterLocale*> flutterLocaleList;
1075  flutterLocaleList.reserve(flutterLocales.size());
1076  std::transform(flutterLocales.begin(), flutterLocales.end(),
1077  std::back_inserter(flutterLocaleList),
1078  [](const auto& arg) -> const auto* { return &arg; });
1079  _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1080 }
1081 
1082 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1083  NSData* messageData = nil;
1084  if (message->message_size > 0) {
1085  messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1086  length:message->message_size
1087  freeWhenDone:NO];
1088  }
1089  NSString* channel = @(message->channel);
1090  __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1091  __block FlutterEngine* weakSelf = self;
1092  NSMutableArray* isResponseValid = self.isResponseValid;
1093  FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1094  _embedderAPI.SendPlatformMessageResponse;
1095  FlutterBinaryReply binaryResponseHandler = ^(NSData* response) {
1096  @synchronized(isResponseValid) {
1097  if (![isResponseValid[0] boolValue]) {
1098  // Ignore, engine was killed.
1099  return;
1100  }
1101  if (responseHandle) {
1102  sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1103  static_cast<const uint8_t*>(response.bytes), response.length);
1104  responseHandle = NULL;
1105  } else {
1106  NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
1107  "on channel '%@'.",
1108  channel);
1109  }
1110  }
1111  };
1112 
1113  FlutterEngineHandlerInfo* handlerInfo = _messengerHandlers[channel];
1114  if (handlerInfo) {
1115  handlerInfo.handler(messageData, binaryResponseHandler);
1116  } else {
1117  binaryResponseHandler(nil);
1118  }
1119 }
1120 
1121 - (void)engineCallbackOnPreEngineRestart {
1122  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1123  FlutterViewController* nextViewController;
1124  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1125  [nextViewController onPreEngineRestart];
1126  }
1127 }
1128 
1129 - (void)onVSync:(uintptr_t)baton {
1130  @synchronized(_vsyncWaiters) {
1131  // TODO(knopp): Use vsync waiter for correct view.
1132  // https://github.com/flutter/flutter/issues/142845
1133  FlutterVSyncWaiter* waiter = [_vsyncWaiters objectForKey:@(kFlutterImplicitViewId)];
1134  [waiter waitForVSync:baton];
1135  }
1136 }
1137 
1138 /**
1139  * Note: Called from dealloc. Should not use accessors or other methods.
1140  */
1141 - (void)shutDownEngine {
1142  if (_engine == nullptr) {
1143  return;
1144  }
1145 
1146  [_threadSynchronizer shutdown];
1147  _threadSynchronizer = nil;
1148 
1149  FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1150  if (result != kSuccess) {
1151  NSLog(@"Could not de-initialize the Flutter engine: error %d", result);
1152  }
1153 
1154  result = _embedderAPI.Shutdown(_engine);
1155  if (result != kSuccess) {
1156  NSLog(@"Failed to shut down Flutter engine: error %d", result);
1157  }
1158  _engine = nullptr;
1159 }
1160 
1161 - (void)setUpPlatformViewChannel {
1163  [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
1164  binaryMessenger:self.binaryMessenger
1165  codec:[FlutterStandardMethodCodec sharedInstance]];
1166 
1167  __weak FlutterEngine* weakSelf = self;
1168  [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1169  [[weakSelf platformViewController] handleMethodCall:call result:result];
1170  }];
1171 }
1172 
1173 - (void)setUpAccessibilityChannel {
1175  messageChannelWithName:@"flutter/accessibility"
1176  binaryMessenger:self.binaryMessenger
1178  __weak FlutterEngine* weakSelf = self;
1179  [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1180  [weakSelf handleAccessibilityEvent:message];
1181  }];
1182 }
1183 - (void)setUpNotificationCenterListeners {
1184  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1185  // macOS fires this private message when VoiceOver turns on or off.
1186  [center addObserver:self
1187  selector:@selector(onAccessibilityStatusChanged:)
1188  name:kEnhancedUserInterfaceNotification
1189  object:nil];
1190  [center addObserver:self
1191  selector:@selector(applicationWillTerminate:)
1192  name:NSApplicationWillTerminateNotification
1193  object:nil];
1194  [center addObserver:self
1195  selector:@selector(windowDidChangeScreen:)
1196  name:NSWindowDidChangeScreenNotification
1197  object:nil];
1198  [center addObserver:self
1199  selector:@selector(updateDisplayConfig:)
1200  name:NSApplicationDidChangeScreenParametersNotification
1201  object:nil];
1202 }
1203 
1204 - (void)addInternalPlugins {
1205  __weak FlutterEngine* weakSelf = self;
1206  [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]
1207  delegate:self];
1208  [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]];
1210  [FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel
1211  binaryMessenger:self.binaryMessenger
1214  [FlutterMethodChannel methodChannelWithName:kFlutterPlatformChannel
1215  binaryMessenger:self.binaryMessenger
1216  codec:[FlutterJSONMethodCodec sharedInstance]];
1217  [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1218  [weakSelf handleMethodCall:call result:result];
1219  }];
1220 }
1221 
1222 - (void)didUpdateMouseCursor:(NSCursor*)cursor {
1223  // Mouse cursor plugin does not specify which view is responsible for changing the cursor,
1224  // so the reasonable assumption here is that cursor change is a result of a mouse movement
1225  // and thus the cursor will be paired with last Flutter view that reveived mouse event.
1226  [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1227 }
1228 
1229 - (void)applicationWillTerminate:(NSNotification*)notification {
1230  [self shutDownEngine];
1231 }
1232 
1233 - (void)windowDidChangeScreen:(NSNotification*)notification {
1234  // Update window metric for all view controllers since the display_id has
1235  // changed.
1236  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1237  FlutterViewController* nextViewController;
1238  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1239  [self updateWindowMetricsForViewController:nextViewController];
1240  }
1241 }
1242 
1243 - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1244  BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1245  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1246  FlutterViewController* nextViewController;
1247  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1248  [nextViewController onAccessibilityStatusChanged:enabled];
1249  }
1250 
1251  self.semanticsEnabled = enabled;
1252 }
1253 - (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent {
1254  NSString* type = annotatedEvent[@"type"];
1255  if ([type isEqualToString:@"announce"]) {
1256  NSString* message = annotatedEvent[@"data"][@"message"];
1257  NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1258  if (message == nil) {
1259  return;
1260  }
1261 
1262  NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1263  ? NSAccessibilityPriorityHigh
1264  : NSAccessibilityPriorityMedium;
1265 
1266  [self announceAccessibilityMessage:message withPriority:priority];
1267  }
1268 }
1269 
1270 - (void)announceAccessibilityMessage:(NSString*)message
1271  withPriority:(NSAccessibilityPriorityLevel)priority {
1272  NSAccessibilityPostNotificationWithUserInfo(
1273  [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView,
1274  NSAccessibilityAnnouncementRequestedNotification,
1275  @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1276 }
1277 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
1278  if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
1279  [[NSApplication sharedApplication] terminate:self];
1280  result(nil);
1281  } else if ([call.method isEqualToString:@"SystemSound.play"]) {
1282  [self playSystemSound:call.arguments];
1283  result(nil);
1284  } else if ([call.method isEqualToString:@"Clipboard.getData"]) {
1285  result([self getClipboardData:call.arguments]);
1286  } else if ([call.method isEqualToString:@"Clipboard.setData"]) {
1287  [self setClipboardData:call.arguments];
1288  result(nil);
1289  } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) {
1290  result(@{@"value" : @([self clipboardHasStrings])});
1291  } else if ([call.method isEqualToString:@"System.exitApplication"]) {
1292  if ([self terminationHandler] == nil) {
1293  // If the termination handler isn't set, then either we haven't
1294  // initialized it yet, or (more likely) the NSApp delegate isn't a
1295  // FlutterAppDelegate, so it can't cancel requests to exit. So, in that
1296  // case, just terminate when requested.
1297  [NSApp terminate:self];
1298  result(nil);
1299  } else {
1300  [[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1301  }
1302  } else if ([call.method isEqualToString:@"System.initializationComplete"]) {
1303  if ([self terminationHandler] != nil) {
1304  [self terminationHandler].acceptingRequests = YES;
1305  }
1306  result(nil);
1307  } else {
1309  }
1310 }
1311 
1312 - (void)playSystemSound:(NSString*)soundType {
1313  if ([soundType isEqualToString:@"SystemSoundType.alert"]) {
1314  NSBeep();
1315  }
1316 }
1317 
1318 - (NSDictionary*)getClipboardData:(NSString*)format {
1319  if ([format isEqualToString:@(kTextPlainFormat)]) {
1320  NSString* stringInPasteboard = [self.pasteboard stringForType:NSPasteboardTypeString];
1321  return stringInPasteboard == nil ? nil : @{@"text" : stringInPasteboard};
1322  }
1323  return nil;
1324 }
1325 
1326 - (void)setClipboardData:(NSDictionary*)data {
1327  NSString* text = data[@"text"];
1328  [self.pasteboard clearContents];
1329  if (text && ![text isEqual:[NSNull null]]) {
1330  [self.pasteboard setString:text forType:NSPasteboardTypeString];
1331  }
1332 }
1333 
1334 - (BOOL)clipboardHasStrings {
1335  return [self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1336 }
1337 
1338 - (std::vector<std::string>)switches {
1340 }
1341 
1342 - (FlutterThreadSynchronizer*)testThreadSynchronizer {
1343  return _threadSynchronizer;
1344 }
1345 
1346 #pragma mark - FlutterAppLifecycleDelegate
1347 
1348 - (void)setApplicationState:(flutter::AppLifecycleState)state {
1349  NSString* nextState =
1350  [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1351  [self sendOnChannel:kFlutterLifecycleChannel
1352  message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1353 }
1354 
1355 /**
1356  * Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
1357  * notification.
1358  */
1359 - (void)handleWillBecomeActive:(NSNotification*)notification {
1360  _active = YES;
1361  if (!_visible) {
1362  [self setApplicationState:flutter::AppLifecycleState::kHidden];
1363  } else {
1364  [self setApplicationState:flutter::AppLifecycleState::kResumed];
1365  }
1366 }
1367 
1368 /**
1369  * Called when the |FlutterAppDelegate| gets the applicationWillResignActive
1370  * notification.
1371  */
1372 - (void)handleWillResignActive:(NSNotification*)notification {
1373  _active = NO;
1374  if (!_visible) {
1375  [self setApplicationState:flutter::AppLifecycleState::kHidden];
1376  } else {
1377  [self setApplicationState:flutter::AppLifecycleState::kInactive];
1378  }
1379 }
1380 
1381 /**
1382  * Called when the |FlutterAppDelegate| gets the applicationDidUnhide
1383  * notification.
1384  */
1385 - (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1386  NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1387  if (occlusionState & NSApplicationOcclusionStateVisible) {
1388  _visible = YES;
1389  if (_active) {
1390  [self setApplicationState:flutter::AppLifecycleState::kResumed];
1391  } else {
1392  [self setApplicationState:flutter::AppLifecycleState::kInactive];
1393  }
1394  } else {
1395  _visible = NO;
1396  [self setApplicationState:flutter::AppLifecycleState::kHidden];
1397  }
1398 }
1399 
1400 #pragma mark - FlutterBinaryMessenger
1401 
1402 - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1403  [self sendOnChannel:channel message:message binaryReply:nil];
1404 }
1405 
1406 - (void)sendOnChannel:(NSString*)channel
1407  message:(NSData* _Nullable)message
1408  binaryReply:(FlutterBinaryReply _Nullable)callback {
1409  FlutterPlatformMessageResponseHandle* response_handle = nullptr;
1410  if (callback) {
1411  struct Captures {
1412  FlutterBinaryReply reply;
1413  };
1414  auto captures = std::make_unique<Captures>();
1415  captures->reply = callback;
1416  auto message_reply = [](const uint8_t* data, size_t data_size, void* user_data) {
1417  auto captures = reinterpret_cast<Captures*>(user_data);
1418  NSData* reply_data = nil;
1419  if (data != nullptr && data_size > 0) {
1420  reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1421  }
1422  captures->reply(reply_data);
1423  delete captures;
1424  };
1425 
1426  FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1427  _engine, message_reply, captures.get(), &response_handle);
1428  if (create_result != kSuccess) {
1429  NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1430  return;
1431  }
1432  captures.release();
1433  }
1434 
1435  FlutterPlatformMessage platformMessage = {
1436  .struct_size = sizeof(FlutterPlatformMessage),
1437  .channel = [channel UTF8String],
1438  .message = static_cast<const uint8_t*>(message.bytes),
1439  .message_size = message.length,
1440  .response_handle = response_handle,
1441  };
1442 
1443  FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1444  if (message_result != kSuccess) {
1445  NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1446  message_result);
1447  }
1448 
1449  if (response_handle != nullptr) {
1450  FlutterEngineResult release_result =
1451  _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1452  if (release_result != kSuccess) {
1453  NSLog(@"Failed to release the response handle (%d).", release_result);
1454  };
1455  }
1456 }
1457 
1458 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel
1459  binaryMessageHandler:
1460  (nullable FlutterBinaryMessageHandler)handler {
1462  _messengerHandlers[channel] =
1463  [[FlutterEngineHandlerInfo alloc] initWithConnection:@(_currentMessengerConnection)
1464  handler:[handler copy]];
1466 }
1467 
1468 - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1469  // Find the _messengerHandlers that has the required connection, and record its
1470  // channel.
1471  NSString* foundChannel = nil;
1472  for (NSString* key in [_messengerHandlers allKeys]) {
1473  FlutterEngineHandlerInfo* handlerInfo = [_messengerHandlers objectForKey:key];
1474  if ([handlerInfo.connection isEqual:@(connection)]) {
1475  foundChannel = key;
1476  break;
1477  }
1478  }
1479  if (foundChannel) {
1480  [_messengerHandlers removeObjectForKey:foundChannel];
1481  }
1482 }
1483 
1484 #pragma mark - FlutterPluginRegistry
1485 
1486 - (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
1487  id<FlutterPluginRegistrar> registrar = self.pluginRegistrars[pluginName];
1488  if (!registrar) {
1489  FlutterEngineRegistrar* registrarImpl =
1490  [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self];
1491  self.pluginRegistrars[pluginName] = registrarImpl;
1492  registrar = registrarImpl;
1493  }
1494  return registrar;
1495 }
1496 
1497 - (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1498  return self.pluginRegistrars[pluginName].publishedValue;
1499 }
1500 
1501 #pragma mark - FlutterTextureRegistrar
1502 
1503 - (int64_t)registerTexture:(id<FlutterTexture>)texture {
1504  return [_renderer registerTexture:texture];
1505 }
1506 
1507 - (BOOL)registerTextureWithID:(int64_t)textureId {
1508  return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1509 }
1510 
1511 - (void)textureFrameAvailable:(int64_t)textureID {
1512  [_renderer textureFrameAvailable:textureID];
1513 }
1514 
1515 - (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1516  return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1517 }
1518 
1519 - (void)unregisterTexture:(int64_t)textureID {
1520  [_renderer unregisterTexture:textureID];
1521 }
1522 
1523 - (BOOL)unregisterTextureWithID:(int64_t)textureID {
1524  return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1525 }
1526 
1527 #pragma mark - Task runner integration
1528 
1529 - (void)runTaskOnEmbedder:(FlutterTask)task {
1530  if (_engine) {
1531  auto result = _embedderAPI.RunTask(_engine, &task);
1532  if (result != kSuccess) {
1533  NSLog(@"Could not post a task to the Flutter engine.");
1534  }
1535  }
1536 }
1537 
1538 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1539  __weak FlutterEngine* weakSelf = self;
1540  auto worker = ^{
1541  [weakSelf runTaskOnEmbedder:task];
1542  };
1543 
1544  const auto engine_time = _embedderAPI.GetCurrentTime();
1545  if (targetTime <= engine_time) {
1546  dispatch_async(dispatch_get_main_queue(), worker);
1547 
1548  } else {
1549  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, targetTime - engine_time),
1550  dispatch_get_main_queue(), worker);
1551  }
1552 }
1553 
1554 // Getter used by test harness, only exposed through the FlutterEngine(Test) category
1555 - (flutter::FlutterCompositor*)macOSCompositor {
1556  return _macOSCompositor.get();
1557 }
1558 
1559 @end
kEnhancedUserInterfaceKey
static NSString *const kEnhancedUserInterfaceKey
Definition: FlutterEngine.mm:57
+[FlutterMouseCursorPlugin registerWithRegistrar:delegate:]
void registerWithRegistrar:delegate:(nonnull id< FlutterPluginRegistrar > registrar,[delegate] nullable id< FlutterMouseCursorPluginDelegate > delegate)
+[FlutterBasicMessageChannel messageChannelWithName:binaryMessenger:codec:]
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
Definition: FlutterChannels.mm:82
_terminator
FlutterTerminationCallback _terminator
Definition: FlutterEngine.mm:188
FlutterPasteboard
Definition: FlutterEngine.mm:286
-[FlutterViewController attached]
BOOL attached()
Definition: FlutterViewController.mm:531
FlutterAppDelegate_Internal.h
_platformChannel
FlutterMethodChannel * _platformChannel
Definition: FlutterEngine.mm:456
FlutterMenuPlugin.h
FlutterEngine
Definition: FlutterEngine.h:31
FlutterMouseCursorPlugin
Definition: FlutterMouseCursorPlugin.h:23
_vsyncWaiters
NSMapTable< NSNumber *, FlutterVSyncWaiter * > * _vsyncWaiters
Definition: FlutterEngine.mm:471
FlutterPlugin-p
Definition: FlutterPluginMacOS.h:29
_platformViewController
FlutterPlatformViewController * _platformViewController
Definition: FlutterEngine.mm:447
kTextPlainFormat
constexpr char kTextPlainFormat[]
Clipboard plain text format.
Definition: FlutterEngine.mm:60
kFlutterPlatformChannel
NSString *const kFlutterPlatformChannel
Definition: FlutterEngine.mm:34
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterBasicMessageChannel
Definition: FlutterChannels.h:37
FlutterTimeConverter.h
FlutterViewController
Definition: FlutterViewController.h:73
FlutterMethodChannel
Definition: FlutterChannels.h:220
FlutterEngine.h
FlutterMethodNotImplemented
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
-[FlutterViewController onAccessibilityStatusChanged:]
void onAccessibilityStatusChanged:(BOOL enabled)
FlutterPlatformViewController
Definition: FlutterPlatformViewController.h:17
FlutterTextureRegistry-p
Definition: FlutterTexture.h:45
FlutterVSyncWaiter.h
FlutterPlatformViewController.h
FlutterEngineHandlerInfo::handler
FlutterBinaryMessageHandler handler
Definition: FlutterEngine.mm:71
user_data
void * user_data
Definition: texture_registrar_unittests.cc:27
_lastViewWithPointerEvent
__weak FlutterView * _lastViewWithPointerEvent
Definition: FlutterEngine.mm:475
FlutterEngine_Internal.h
+[FlutterDartProject lookupKeyForAsset:]
NSString * lookupKeyForAsset:(NSString *asset)
Definition: FlutterDartProject.mm:116
FlutterError
Definition: FlutterCodecs.h:246
FlutterViewEngineProvider.h
flutter::FlutterCompositor
Definition: FlutterCompositor.h:36
_currentMessengerConnection
FlutterBinaryMessengerConnection _currentMessengerConnection
Definition: FlutterEngine.mm:421
FlutterMethodCall::method
NSString * method
Definition: FlutterCodecs.h:233
kMainThreadPriority
static const int kMainThreadPriority
Definition: FlutterEngine.mm:482
_settingsChannel
FlutterBasicMessageChannel * _settingsChannel
Definition: FlutterEngine.mm:450
FlutterRenderer.h
_project
FlutterDartProject * _project
Definition: FlutterEngine.mm:409
FlutterVSyncWaiter
Definition: FlutterVSyncWaiter.h:8
FlutterViewController::engine
FlutterEngine * engine
Definition: FlutterViewController.h:78
FlutterEngineRegistrar::publishedValue
NSObject * publishedValue
Definition: FlutterEngine.mm:318
FlutterMenuPlugin
Definition: FlutterMenuPlugin.h:20
FlutterPluginRegistrar-p
Definition: FlutterPluginRegistrarMacOS.h:28
FlutterAppLifecycleProvider-p
Definition: FlutterAppDelegate.h:21
_viewControllers
NSMapTable * _viewControllers
Definition: FlutterEngine.mm:436
FlutterEngine::binaryMessenger
id< FlutterBinaryMessenger > binaryMessenger
Definition: FlutterEngine.h:92
app_lifecycle_state.h
FlutterViewEngineProvider
Definition: FlutterViewEngineProvider.h:18
_macOSCompositor
std::unique_ptr< flutter::FlutterCompositor > _macOSCompositor
Definition: FlutterEngine.mm:431
FlutterAppLifecycleDelegate-p
Definition: FlutterAppLifecycleDelegate.h:21
FlutterRenderer
Definition: FlutterRenderer.h:18
FlutterBinaryMessageHandler
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
Definition: FlutterBinaryMessenger.h:30
-[FlutterVSyncWaiter waitForVSync:]
void waitForVSync:(uintptr_t baton)
Definition: FlutterVSyncWaiter.mm:108
kEnhancedUserInterfaceNotification
static NSString *const kEnhancedUserInterfaceNotification
The private notification for voice over.
Definition: FlutterEngine.mm:55
FlutterMouseCursorPlugin.h
-[FlutterMethodChannel setMethodCallHandler:]
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
FlutterStandardMessageCodec
Definition: FlutterCodecs.h:209
FlutterBinaryMessengerRelay.h
-[FlutterViewController onPreEngineRestart]
void onPreEngineRestart()
Definition: FlutterViewController.mm:490
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterHourFormat
Definition: FlutterHourFormat.h:10
FlutterThreadSynchronizer
Definition: FlutterThreadSynchronizer.h:18
flutter
Definition: AccessibilityBridgeMac.h:16
FlutterMouseCursorPluginDelegate-p
Definition: FlutterMouseCursorPlugin.h:13
flutter::GetSwitchesFromEnvironment
std::vector< std::string > GetSwitchesFromEnvironment()
Definition: engine_switches.cc:14
FlutterAppDelegate
Definition: FlutterAppDelegate.h:54
engine_switches.h
_visible
BOOL _visible
Definition: FlutterEngine.mm:464
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:194
OnPlatformMessage
static void OnPlatformMessage(const FlutterPlatformMessage *message, void *user_data)
Definition: FlutterEngine.mm:402
FlutterAppDelegate.h
FlutterTimeConverter
Converts between the time representation used by Flutter Engine and CAMediaTime.
Definition: FlutterTimeConverter.h:13
kFlutterLifecycleChannel
NSString *const kFlutterLifecycleChannel
Definition: FlutterEngine.mm:36
_active
BOOL _active
Definition: FlutterEngine.mm:461
-[FlutterPlugin-p handleMethodCall:result:]
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
_binaryMessenger
FlutterBinaryMessengerRelay * _binaryMessenger
Definition: FlutterEngine.mm:467
+[FlutterMethodChannel methodChannelWithName:binaryMessenger:codec:]
instancetype methodChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
-[FlutterTimeConverter CAMediaTimeToEngineTime:]
uint64_t CAMediaTimeToEngineTime:(CFTimeInterval time)
Definition: FlutterTimeConverter.mm:26
FlutterPlatformViewFactory-p
Definition: FlutterPlatformViews.h:13
_messengerHandlers
NSMutableDictionary< NSString *, FlutterEngineHandlerInfo * > * _messengerHandlers
Definition: FlutterEngine.mm:417
_aotData
_FlutterEngineAOTData * _aotData
Definition: FlutterEngine.mm:427
FlutterDartProject_Internal.h
FlutterViewController_Internal.h
_accessibilityChannel
FlutterBasicMessageChannel * _accessibilityChannel
Definition: FlutterEngine.mm:453
kFlutterSettingsChannel
NSString *const kFlutterSettingsChannel
Definition: FlutterEngine.mm:35
_compositor
FlutterCompositor _compositor
Definition: FlutterEngine.mm:439
+[FlutterMenuPlugin registerWithRegistrar:]
void registerWithRegistrar:(nonnull id< FlutterPluginRegistrar > registrar)
Definition: FlutterMenuPlugin.mm:412
-[FlutterPasteboard clearContents]
NSInteger clearContents()
Definition: FlutterEngine.mm:288
-[FlutterEngineTerminationHandler requestApplicationTermination:exitType:result:]
void requestApplicationTermination:exitType:result:(NSApplication *sender,[exitType] FlutterAppExitType type,[result] nullable FlutterResult result)
FlutterViewController::viewIdentifier
FlutterViewIdentifier viewIdentifier
Definition: FlutterViewController.h:130
FlutterView
Definition: FlutterView.h:35
FlutterBinaryMessengerRelay
Definition: FlutterBinaryMessengerRelay.h:14
FlutterJSONMethodCodec
Definition: FlutterCodecs.h:455
+[FlutterDartProject lookupKeyForAsset:fromPackage:]
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
Definition: FlutterDartProject.mm:125
FlutterEngineRegistrar
Definition: FlutterEngine.mm:307
FlutterTexture-p
Definition: FlutterTexture.h:21
+[FlutterHourFormat isAlwaysUse24HourFormat]
BOOL isAlwaysUse24HourFormat()
Definition: FlutterHourFormat.mm:8
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
FlutterDartProject
Definition: FlutterDartProject.mm:24
FlutterEngineHandlerInfo
Definition: FlutterEngine.mm:66
_allowHeadlessExecution
BOOL _allowHeadlessExecution
Definition: FlutterEngine.mm:424
FlutterBinaryMessenger-p
Definition: FlutterBinaryMessenger.h:49
FlutterEngineTerminationHandler
Definition: FlutterEngine.mm:188
FlutterEngineHandlerInfo::connection
NSNumber * connection
Definition: FlutterEngine.mm:72
_flutterEngine
__weak FlutterEngine * _flutterEngine
Definition: FlutterEngine.mm:321
_connection
FlutterBinaryMessengerConnection _connection
Definition: FlutterChannels.mm:72
FlutterLocaleFromNSLocale
static FlutterLocale FlutterLocaleFromNSLocale(NSLocale *locale)
Definition: FlutterEngine.mm:44
FlutterViewIdentifier
int64_t FlutterViewIdentifier
Definition: FlutterViewController.h:21
FlutterStandardMethodCodec
Definition: FlutterCodecs.h:469
FlutterBinaryMessengerConnection
int64_t FlutterBinaryMessengerConnection
Definition: FlutterBinaryMessenger.h:32
FlutterCompositor.h
_threadSynchronizer
FlutterThreadSynchronizer * _threadSynchronizer
Definition: FlutterEngine.mm:458
FlutterBinaryReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
FlutterMethodCall::arguments
id arguments
Definition: FlutterCodecs.h:238
+[FlutterMessageCodec-p sharedInstance]
instancetype sharedInstance()
_platformViewsChannel
FlutterMethodChannel * _platformViewsChannel
Definition: FlutterEngine.mm:443
FlutterJSONMessageCodec
Definition: FlutterCodecs.h:81
FlutterTerminationCallback
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterTerminationCallback)(id _Nullable sender)