Flutter iOS Embedder
FlutterPlatformViewsController.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 
6 #include "display_list/geometry/dl_geometry_types.h"
7 #include "impeller/geometry/rounding_radii.h"
8 
9 #include "flutter/display_list/effects/image_filters/dl_blur_image_filter.h"
10 #include "flutter/display_list/utils/dl_matrix_clip_tracker.h"
11 #include "flutter/flow/surface_frame.h"
12 #include "flutter/flow/view_slicer.h"
13 #include "flutter/fml/make_copyable.h"
14 #include "flutter/fml/synchronization/count_down_latch.h"
19 
20 using flutter::DlISize;
21 using flutter::DlMatrix;
22 using flutter::DlRect;
23 using flutter::DlRoundRect;
24 
25 static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
26 
27 struct LayerData {
28  DlRect rect;
29  int64_t view_id;
30  int64_t overlay_id;
31  std::shared_ptr<flutter::OverlayLayer> layer;
32 };
33 using LayersMap = std::unordered_map<int64_t, LayerData>;
34 
35 /// Each of the following structs stores part of the platform view hierarchy according to its
36 /// ID.
37 ///
38 /// This data must only be accessed on the platform thread.
40  NSObject<FlutterPlatformView>* view;
42  UIView* root_view;
43 };
44 
45 // Converts a DlMatrix to CATransform3D.
46 static CATransform3D GetCATransform3DFromDlMatrix(const DlMatrix& matrix) {
47  CATransform3D transform = CATransform3DIdentity;
48  transform.m11 = matrix.m[0];
49  transform.m12 = matrix.m[1];
50  transform.m13 = matrix.m[2];
51  transform.m14 = matrix.m[3];
52 
53  transform.m21 = matrix.m[4];
54  transform.m22 = matrix.m[5];
55  transform.m23 = matrix.m[6];
56  transform.m24 = matrix.m[7];
57 
58  transform.m31 = matrix.m[8];
59  transform.m32 = matrix.m[9];
60  transform.m33 = matrix.m[10];
61  transform.m34 = matrix.m[11];
62 
63  transform.m41 = matrix.m[12];
64  transform.m42 = matrix.m[13];
65  transform.m43 = matrix.m[14];
66  transform.m44 = matrix.m[15];
67  return transform;
68 }
69 
70 // Reset the anchor of `layer` to match the transform operation from flow.
71 //
72 // The position of the `layer` should be unchanged after resetting the anchor.
73 static void ResetAnchor(CALayer* layer) {
74  // Flow uses (0, 0) to apply transform matrix so we need to match that in Quartz.
75  layer.anchorPoint = CGPointZero;
76  layer.position = CGPointZero;
77 }
78 
79 static CGRect GetCGRectFromDlRect(const DlRect& clipDlRect) {
80  return CGRectMake(clipDlRect.GetLeft(), //
81  clipDlRect.GetTop(), //
82  clipDlRect.GetWidth(), //
83  clipDlRect.GetHeight());
84 }
85 
87 
88 // The pool of reusable view layers. The pool allows to recycle layer in each frame.
89 @property(nonatomic, readonly) flutter::OverlayLayerPool* layerPool;
90 
91 // The platform view's |EmbedderViewSlice| keyed off the view id, which contains any subsequent
92 // operation until the next platform view or the end of the last leaf node in the layer tree.
93 //
94 // The Slices are deleted by the PlatformViewsController.reset().
95 @property(nonatomic, readonly)
96  std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>>& slices;
97 
98 @property(nonatomic, readonly) FlutterClippingMaskViewPool* maskViewPool;
99 
100 @property(nonatomic, readonly)
101  std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*>& factories;
102 
103 // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view.
104 @property(nonatomic, readonly)
105  std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>&
106  gestureRecognizersBlockingPolicies;
107 
108 /// The size of the current onscreen surface in physical pixels.
109 @property(nonatomic, assign) DlISize frameSize;
110 
111 /// The task runner for posting tasks to the platform thread.
112 @property(nonatomic, readonly) const fml::RefPtr<fml::TaskRunner>& platformTaskRunner;
113 
114 /// This data must only be accessed on the platform thread.
115 @property(nonatomic, readonly) std::unordered_map<int64_t, PlatformViewData>& platformViews;
116 
117 /// The composition parameters for each platform view.
118 ///
119 /// This state is only modified on the raster thread.
120 @property(nonatomic, readonly)
121  std::unordered_map<int64_t, flutter::EmbeddedViewParams>& currentCompositionParams;
122 
123 /// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on
124 /// the next frame.
125 ///
126 /// This state is modified on both the platform and raster thread.
127 @property(nonatomic, readonly) std::unordered_set<int64_t>& viewsToDispose;
128 
129 /// view IDs in composition order.
130 ///
131 /// This state is only modified on the raster thread.
132 @property(nonatomic, readonly) std::vector<int64_t>& compositionOrder;
133 
134 /// platform view IDs visited during layer tree composition.
135 ///
136 /// This state is only modified on the raster thread.
137 @property(nonatomic, readonly) std::vector<int64_t>& visitedPlatformViews;
138 
139 /// Only composite platform views in this set.
140 ///
141 /// This state is only modified on the raster thread.
142 @property(nonatomic, readonly) std::unordered_set<int64_t>& viewsToRecomposite;
143 
144 /// @brief The composition order from the previous thread.
145 ///
146 /// Only accessed from the platform thread.
147 @property(nonatomic, readonly) std::vector<int64_t>& previousCompositionOrder;
148 
149 /// Whether the previous frame had any platform views in active composition order.
150 ///
151 /// This state is tracked so that the first frame after removing the last platform view
152 /// runs through the platform view rendering code path, giving us a chance to remove the
153 /// platform view from the UIView hierarchy.
154 ///
155 /// Only accessed from the raster thread.
156 @property(nonatomic, assign) BOOL hadPlatformViews;
157 
158 /// Whether blurred backdrop filters can be applied.
159 ///
160 /// Defaults to YES, but becomes NO if blurred backdrop filters cannot be applied.
161 @property(nonatomic, assign) BOOL canApplyBlurBackdrop;
162 
163 /// Populate any missing overlay layers.
164 ///
165 /// This requires posting a task to the platform thread and blocking on its completion.
166 - (void)createMissingOverlays:(size_t)requiredOverlayLayers
167  withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext;
168 
169 /// Update the buffers and mutate the platform views in CATransaction on the platform thread.
170 - (void)performSubmit:(const LayersMap&)platformViewLayers
171  currentCompositionParams:
172  (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
173  viewsToRecomposite:(const std::unordered_set<int64_t>&)viewsToRecomposite
174  compositionOrder:(const std::vector<int64_t>&)compositionOrder
175  unusedLayers:
176  (const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
177  surfaceFrames:
178  (const std::vector<std::unique_ptr<flutter::SurfaceFrame>>&)surfaceFrames;
179 
180 - (void)onCreate:(FlutterMethodCall*)call result:(FlutterResult)result;
181 - (void)onDispose:(FlutterMethodCall*)call result:(FlutterResult)result;
182 - (void)onAcceptGesture:(FlutterMethodCall*)call result:(FlutterResult)result;
183 - (void)onRejectGesture:(FlutterMethodCall*)call result:(FlutterResult)result;
184 
185 - (void)clipViewSetMaskView:(UIView*)clipView;
186 
187 // Applies the mutators in the mutatorsStack to the UIView chain that was constructed by
188 // `ReconstructClipViewsChain`
189 //
190 // Clips are applied to the `embeddedView`'s super view(|ChildClippingView|) using a
191 // |FlutterClippingMaskView|. Transforms are applied to `embeddedView`
192 //
193 // The `boundingRect` is the final bounding rect of the PlatformView
194 // (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding
195 // rect of the PlatformView, the clip mutator is not applied for performance optimization.
196 //
197 // This method is only called when the `embeddedView` needs to be re-composited at the current
198 // frame. See: `compositeView:withParams:` for details.
199 - (void)applyMutators:(const flutter::MutatorsStack&)mutatorsStack
200  embeddedView:(UIView*)embeddedView
201  boundingRect:(const DlRect&)boundingRect;
202 
203 // Appends the overlay views and platform view and sets their z index based on the composition
204 // order.
205 - (void)bringLayersIntoView:(const LayersMap&)layerMap
206  withCompositionOrder:(const std::vector<int64_t>&)compositionOrder;
207 
208 - (std::shared_ptr<flutter::OverlayLayer>)nextLayerInPool;
209 
210 /// Runs on the platform thread.
211 - (void)createLayerWithIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
212  pixelFormat:(MTLPixelFormat)pixelFormat;
213 
214 /// Removes overlay views and platform views that aren't needed in the current frame.
215 /// Must run on the platform thread.
216 - (void)removeUnusedLayers:(const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
217  withCompositionOrder:(const std::vector<int64_t>&)compositionOrder;
218 
219 /// Computes and returns all views to be disposed on the platform thread, removes them from
220 /// self.platformViews, self.viewsToRecomposite, and self.currentCompositionParams. Any views that
221 /// still require compositing are not returned, but instead added to `viewsToDelayDispose` for
222 /// disposal on the next call.
223 - (std::vector<UIView*>)computeViewsToDispose;
224 
225 /// Resets the state of the frame.
226 - (void)resetFrameState;
227 @end
228 
229 @implementation FlutterPlatformViewsController {
230  // TODO(cbracken): Replace with Obj-C types and use @property declarations to automatically
231  // synthesize the ivars.
232  //
233  // These ivars are required because we're transitioning the previous C++ implementation to Obj-C.
234  // We require ivars to declare the concrete types and then wrap with @property declarations that
235  // return a reference to the ivar, allowing for use like `self.layerPool` and
236  // `self.slices[viewId] = x`.
237  std::unique_ptr<flutter::OverlayLayerPool> _layerPool;
238  std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>> _slices;
239  std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*> _factories;
240  std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>
242  fml::RefPtr<fml::TaskRunner> _platformTaskRunner;
243  std::unordered_map<int64_t, PlatformViewData> _platformViews;
244  std::unordered_map<int64_t, flutter::EmbeddedViewParams> _currentCompositionParams;
245  std::unordered_set<int64_t> _viewsToDispose;
246  std::vector<int64_t> _compositionOrder;
247  std::vector<int64_t> _visitedPlatformViews;
248  std::unordered_set<int64_t> _viewsToRecomposite;
249  std::vector<int64_t> _previousCompositionOrder;
250 }
251 
252 - (id)init {
253  if (self = [super init]) {
254  _layerPool = std::make_unique<flutter::OverlayLayerPool>();
255  _maskViewPool =
256  [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity];
257  _hadPlatformViews = NO;
258  _canApplyBlurBackdrop = YES;
259  }
260  return self;
261 }
262 
263 - (const fml::RefPtr<fml::TaskRunner>&)taskRunner {
264  return _platformTaskRunner;
265 }
266 
267 - (void)setTaskRunner:(const fml::RefPtr<fml::TaskRunner>&)platformTaskRunner {
268  _platformTaskRunner = platformTaskRunner;
269 }
270 
271 - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
272  if ([[call method] isEqualToString:@"create"]) {
273  [self onCreate:call result:result];
274  } else if ([[call method] isEqualToString:@"dispose"]) {
275  [self onDispose:call result:result];
276  } else if ([[call method] isEqualToString:@"acceptGesture"]) {
277  [self onAcceptGesture:call result:result];
278  } else if ([[call method] isEqualToString:@"rejectGesture"]) {
279  [self onRejectGesture:call result:result];
280  } else {
282  }
283 }
284 
285 - (void)onCreate:(FlutterMethodCall*)call result:(FlutterResult)result {
286  NSDictionary<NSString*, id>* args = [call arguments];
287 
288  int64_t viewId = [args[@"id"] longLongValue];
289  NSString* viewTypeString = args[@"viewType"];
290  std::string viewType(viewTypeString.UTF8String);
291 
292  if (self.platformViews.count(viewId) != 0) {
293  result([FlutterError errorWithCode:@"recreating_view"
294  message:@"trying to create an already created view"
295  details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
296  return;
297  }
298 
299  NSObject<FlutterPlatformViewFactory>* factory = self.factories[viewType];
300  if (factory == nil) {
301  result([FlutterError
302  errorWithCode:@"unregistered_view_type"
303  message:[NSString stringWithFormat:@"A UIKitView widget is trying to create a "
304  @"PlatformView with an unregistered type: < %@ >",
305  viewTypeString]
306  details:@"If you are the author of the PlatformView, make sure `registerViewFactory` "
307  @"is invoked.\n"
308  @"See: "
309  @"https://docs.flutter.cn/development/platform-integration/"
310  @"platform-views#on-the-platform-side-1 for more details.\n"
311  @"If you are not the author of the PlatformView, make sure to call "
312  @"`GeneratedPluginRegistrant.register`."]);
313  return;
314  }
315 
316  id params = nil;
317  if ([factory respondsToSelector:@selector(createArgsCodec)]) {
318  NSObject<FlutterMessageCodec>* codec = [factory createArgsCodec];
319  if (codec != nil && args[@"params"] != nil) {
320  FlutterStandardTypedData* paramsData = args[@"params"];
321  params = [codec decode:paramsData.data];
322  }
323  }
324 
325  NSObject<FlutterPlatformView>* embeddedView = [factory createWithFrame:CGRectZero
326  viewIdentifier:viewId
327  arguments:params];
328  UIView* platformView = [embeddedView view];
329  // Set a unique view identifier, so the platform view can be identified in unit tests.
330  platformView.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld]", viewId];
331 
333  initWithEmbeddedView:platformView
334  platformViewsController:self
335  gestureRecognizersBlockingPolicy:self.gestureRecognizersBlockingPolicies[viewType]];
336 
337  ChildClippingView* clippingView = [[ChildClippingView alloc] initWithFrame:CGRectZero];
338  [clippingView addSubview:touchInterceptor];
339 
340  self.platformViews.emplace(viewId, PlatformViewData{
341  .view = embeddedView, //
342  .touch_interceptor = touchInterceptor, //
343  .root_view = clippingView //
344  });
345 
346  result(nil);
347 }
348 
349 - (void)onDispose:(FlutterMethodCall*)call result:(FlutterResult)result {
350  NSNumber* arg = [call arguments];
351  int64_t viewId = [arg longLongValue];
352 
353  if (self.platformViews.count(viewId) == 0) {
354  result([FlutterError errorWithCode:@"unknown_view"
355  message:@"trying to dispose an unknown"
356  details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
357  return;
358  }
359  // We wait for next submitFrame to dispose views.
360  self.viewsToDispose.insert(viewId);
361  result(nil);
362 }
363 
364 - (void)onAcceptGesture:(FlutterMethodCall*)call result:(FlutterResult)result {
365  NSDictionary<NSString*, id>* args = [call arguments];
366  int64_t viewId = [args[@"id"] longLongValue];
367 
368  if (self.platformViews.count(viewId) == 0) {
369  result([FlutterError errorWithCode:@"unknown_view"
370  message:@"trying to set gesture state for an unknown view"
371  details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
372  return;
373  }
374 
375  FlutterTouchInterceptingView* view = self.platformViews[viewId].touch_interceptor;
376  [view releaseGesture];
377 
378  result(nil);
379 }
380 
381 - (void)onRejectGesture:(FlutterMethodCall*)call result:(FlutterResult)result {
382  NSDictionary<NSString*, id>* args = [call arguments];
383  int64_t viewId = [args[@"id"] longLongValue];
384 
385  if (self.platformViews.count(viewId) == 0) {
386  result([FlutterError errorWithCode:@"unknown_view"
387  message:@"trying to set gesture state for an unknown view"
388  details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
389  return;
390  }
391 
392  FlutterTouchInterceptingView* view = self.platformViews[viewId].touch_interceptor;
393  [view blockGesture];
394 
395  result(nil);
396 }
397 
398 - (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
399  withId:(NSString*)factoryId
400  gestureRecognizersBlockingPolicy:
401  (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizerBlockingPolicy {
402  std::string idString([factoryId UTF8String]);
403  FML_CHECK(self.factories.count(idString) == 0);
404  self.factories[idString] = factory;
405  self.gestureRecognizersBlockingPolicies[idString] = gestureRecognizerBlockingPolicy;
406 }
407 
408 - (void)beginFrameWithSize:(DlISize)frameSize {
409  [self resetFrameState];
410  self.frameSize = frameSize;
411 }
412 
413 - (void)cancelFrame {
414  [self resetFrameState];
415 }
416 
417 - (flutter::PostPrerollResult)postPrerollActionWithThreadMerger:
418  (const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger {
419  return flutter::PostPrerollResult::kSuccess;
420 }
421 
422 - (void)endFrameWithResubmit:(BOOL)shouldResubmitFrame
423  threadMerger:(const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger {
424 }
425 
426 - (void)pushFilterToVisitedPlatformViews:(const std::shared_ptr<flutter::DlImageFilter>&)filter
427  withRect:(const flutter::DlRect&)filterRect {
428  for (int64_t id : self.visitedPlatformViews) {
429  flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
430  params.PushImageFilter(filter, filterRect);
431  self.currentCompositionParams[id] = params;
432  }
433 }
434 
435 - (void)prerollCompositeEmbeddedView:(int64_t)viewId
436  withParams:(std::unique_ptr<flutter::EmbeddedViewParams>)params {
437  DlRect viewBounds = DlRect::MakeSize(self.frameSize);
438  std::unique_ptr<flutter::EmbedderViewSlice> view;
439  view = std::make_unique<flutter::DisplayListEmbedderViewSlice>(viewBounds);
440  self.slices.insert_or_assign(viewId, std::move(view));
441 
442  self.compositionOrder.push_back(viewId);
443 
444  if (self.currentCompositionParams.count(viewId) == 1 &&
445  self.currentCompositionParams[viewId] == *params.get()) {
446  // Do nothing if the params didn't change.
447  return;
448  }
449  self.currentCompositionParams[viewId] = flutter::EmbeddedViewParams(*params.get());
450  self.viewsToRecomposite.insert(viewId);
451 }
452 
453 - (size_t)embeddedViewCount {
454  return self.compositionOrder.size();
455 }
456 
457 - (UIView*)platformViewForId:(int64_t)viewId {
458  return [self flutterTouchInterceptingViewForId:viewId].embeddedView;
459 }
460 
461 - (FlutterTouchInterceptingView*)flutterTouchInterceptingViewForId:(int64_t)viewId {
462  if (self.platformViews.empty()) {
463  return nil;
464  }
465  return self.platformViews[viewId].touch_interceptor;
466 }
467 
468 - (long)firstResponderPlatformViewId {
469  for (auto const& [id, platformViewData] : self.platformViews) {
470  UIView* rootView = platformViewData.root_view;
471  if (rootView.flt_hasFirstResponderInViewHierarchySubtree) {
472  return id;
473  }
474  }
475  return -1;
476 }
477 
478 - (void)clipViewSetMaskView:(UIView*)clipView {
479  FML_DCHECK([[NSThread currentThread] isMainThread]);
480  if (clipView.maskView) {
481  return;
482  }
483  CGRect frame =
484  CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y,
485  CGRectGetWidth(self.flutterView.bounds), CGRectGetHeight(self.flutterView.bounds));
486  clipView.maskView = [self.maskViewPool getMaskViewWithFrame:frame];
487 }
488 
489 - (void)applyMutators:(const flutter::MutatorsStack&)mutatorsStack
490  embeddedView:(UIView*)embeddedView
491  boundingRect:(const DlRect&)boundingRect {
492  if (self.flutterView == nil) {
493  return;
494  }
495 
496  ResetAnchor(embeddedView.layer);
497  ChildClippingView* clipView = (ChildClippingView*)embeddedView.superview;
498 
499  DlMatrix transformMatrix;
500  NSMutableArray* blurFilters = [[NSMutableArray alloc] init];
501  NSMutableArray<PendingRRectClip*>* pendingClipRRects = [[NSMutableArray alloc] init];
502 
503  FML_DCHECK(!clipView.maskView ||
504  [clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]);
505  if (clipView.maskView) {
506  [self.maskViewPool insertViewToPoolIfNeeded:(FlutterClippingMaskView*)(clipView.maskView)];
507  clipView.maskView = nil;
508  }
509  CGFloat screenScale = [UIScreen mainScreen].scale;
510  auto iter = mutatorsStack.Begin();
511  while (iter != mutatorsStack.End()) {
512  switch ((*iter)->GetType()) {
513  case flutter::MutatorType::kTransform: {
514  transformMatrix = transformMatrix * (*iter)->GetMatrix();
515  break;
516  }
517  case flutter::MutatorType::kClipRect: {
518  if (flutter::DisplayListMatrixClipState::TransformedRectCoversBounds(
519  (*iter)->GetRect(), transformMatrix, boundingRect)) {
520  break;
521  }
522  [self clipViewSetMaskView:clipView];
523  [(FlutterClippingMaskView*)clipView.maskView clipRect:(*iter)->GetRect()
524  matrix:transformMatrix];
525  break;
526  }
527  case flutter::MutatorType::kClipRRect: {
528  if (flutter::DisplayListMatrixClipState::TransformedRRectCoversBounds(
529  (*iter)->GetRRect(), transformMatrix, boundingRect)) {
530  break;
531  }
532  [self clipViewSetMaskView:clipView];
533  [(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRRect()
534  matrix:transformMatrix];
535  break;
536  }
537  case flutter::MutatorType::kClipRSE: {
538  if (flutter::DisplayListMatrixClipState::TransformedRoundSuperellipseCoversBounds(
539  (*iter)->GetRSE(), transformMatrix, boundingRect)) {
540  break;
541  }
542  [self clipViewSetMaskView:clipView];
543  [(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRSEApproximation()
544  matrix:transformMatrix];
545  break;
546  }
547  case flutter::MutatorType::kClipPath: {
548  // TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning
549  // rect. See `ClipRRectContainsPlatformViewBoundingRect`.
550  // https://github.com/flutter/flutter/issues/118650
551  [self clipViewSetMaskView:clipView];
552  [(FlutterClippingMaskView*)clipView.maskView clipPath:(*iter)->GetPath()
553  matrix:transformMatrix];
554  break;
555  }
556  case flutter::MutatorType::kOpacity:
557  embeddedView.alpha = (*iter)->GetAlphaFloat() * embeddedView.alpha;
558  break;
559  case flutter::MutatorType::kBackdropFilter: {
560  // Only support DlBlurImageFilter for BackdropFilter.
561  if (!self.canApplyBlurBackdrop || !(*iter)->GetFilterMutation().GetFilter().asBlur()) {
562  break;
563  }
564  CGRect filterRect = GetCGRectFromDlRect((*iter)->GetFilterMutation().GetFilterRect());
565  // `filterRect` is in global coordinates. We need to convert to local space.
566  filterRect = CGRectApplyAffineTransform(
567  filterRect, CGAffineTransformMakeScale(1 / screenScale, 1 / screenScale));
568  // `filterRect` reprents the rect that should be filtered inside the `_flutterView`.
569  // The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be
570  // filtered.
571  if (CGRectIsNull(CGRectIntersection(filterRect, clipView.frame))) {
572  break;
573  }
574  CGRect intersection = CGRectIntersection(filterRect, clipView.frame);
575  CGRect frameInClipView = [self.flutterView convertRect:intersection toView:clipView];
576  // sigma_x is arbitrarily chosen as the radius value because Quartz sets
577  // sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
578  // is not supported in Quartz's gaussianBlur CAFilter, so it is not used
579  // to blur the PlatformView.
580  CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x();
581  UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
582  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
583 
584  // TODO(https://github.com/flutter/flutter/issues/179126)
585  CGFloat cornerRadius = 0.0;
586  BOOL isRoundedSuperellipse = NO;
587  // If there's multiple clips, this uses the innermost to decide if its
588  // rse or not. The assumption being the innermost will be the tightest
589  if ([pendingClipRRects count] > 0) {
590  cornerRadius = pendingClipRRects.lastObject.topLeftRadius;
591  isRoundedSuperellipse = pendingClipRRects.lastObject.isRoundedSuperellipse;
592  [pendingClipRRects removeAllObjects];
593  }
594  visualEffectView.layer.cornerRadius = cornerRadius;
595  if (@available(iOS 13.0, *)) {
596  visualEffectView.layer.cornerCurve =
597  isRoundedSuperellipse ? kCACornerCurveContinuous : kCACornerCurveCircular;
598  }
599  visualEffectView.clipsToBounds = YES;
600 
601  PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView
602  blurRadius:blurRadius
603  cornerRadius:cornerRadius
604  isRoundedSuperellipse:isRoundedSuperellipse
605  visualEffectView:visualEffectView];
606  if (!filter) {
607  self.canApplyBlurBackdrop = NO;
608  } else {
609  [blurFilters addObject:filter];
610  }
611  break;
612  }
613  case flutter::MutatorType::kBackdropClipRect: {
614  // The frame already handles cropping into the rect so this can
615  // no-op
616  break;
617  }
618  case flutter::MutatorType::kBackdropClipRRect: {
619  PendingRRectClip* clip = [[PendingRRectClip alloc] init];
620  DlRoundRect rrect = (*iter)->GetBackdropClipRRect().rrect;
621 
622  clip.rect = boundingRect;
623  impeller::RoundingRadii radii = rrect.GetRadii();
624  clip.topLeftRadius = radii.top_left.width;
625  clip.topRightRadius = radii.top_right.width;
626  clip.bottomLeftRadius = radii.bottom_left.width;
627  clip.bottomRightRadius = radii.bottom_right.width;
628  [pendingClipRRects addObject:clip];
629  break;
630  }
631  case flutter::MutatorType::kBackdropClipRSuperellipse: {
632  PendingRRectClip* clip = [[PendingRRectClip alloc] init];
633  flutter::DlRoundSuperellipse rse = (*iter)->GetBackdropClipRSuperellipse().rse;
634 
635  clip.rect = boundingRect;
636  impeller::RoundingRadii radii = rse.GetRadii();
637  clip.topLeftRadius = radii.top_left.width;
638  clip.topRightRadius = radii.top_right.width;
639  clip.bottomLeftRadius = radii.bottom_left.width;
640  clip.bottomRightRadius = radii.bottom_right.width;
641  clip.isRoundedSuperellipse = YES;
642  [pendingClipRRects addObject:clip];
643  break;
644  }
645  case flutter::MutatorType::kBackdropClipPath: {
646  // TODO(https://github.com/flutter/flutter/issues/179127)
647  break;
648  }
649  }
650  ++iter;
651  }
652 
653  if (self.canApplyBlurBackdrop) {
654  [clipView applyBlurBackdropFilters:blurFilters];
655  }
656 
657  // The UIKit frame is set based on the logical resolution (points) instead of physical.
658  // (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
659  // However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
660  // 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
661  // down to the logical resoltion before applying it to the layer of PlatformView.
662  flutter::DlScalar pointScale = 1.0 / screenScale;
663  transformMatrix = DlMatrix::MakeScale({pointScale, pointScale, 1}) * transformMatrix;
664 
665  // Reverse the offset of the clipView.
666  // The clipView's frame includes the final translate of the final transform matrix.
667  // Thus, this translate needs to be reversed so the platform view can layout at the correct
668  // offset.
669  //
670  // Note that the transforms are not applied to the clipping paths because clipping paths happen on
671  // the mask view, whose origin is always (0,0) to the _flutterView.
672  impeller::Vector3 origin = impeller::Vector3(clipView.frame.origin.x, clipView.frame.origin.y);
673  transformMatrix = DlMatrix::MakeTranslation(-origin) * transformMatrix;
674 
675  embeddedView.layer.transform = GetCATransform3DFromDlMatrix(transformMatrix);
676 }
677 
678 - (void)compositeView:(int64_t)viewId withParams:(const flutter::EmbeddedViewParams&)params {
679  // TODO(https://github.com/flutter/flutter/issues/109700)
680  CGRect frame = CGRectMake(0, 0, params.sizePoints().width, params.sizePoints().height);
681  FlutterTouchInterceptingView* touchInterceptor = self.platformViews[viewId].touch_interceptor;
682  touchInterceptor.layer.transform = CATransform3DIdentity;
683  touchInterceptor.frame = frame;
684  touchInterceptor.alpha = 1;
685 
686  const flutter::MutatorsStack& mutatorStack = params.mutatorsStack();
687  UIView* clippingView = self.platformViews[viewId].root_view;
688  // The frame of the clipping view should be the final bounding rect.
689  // Because the translate matrix in the Mutator Stack also includes the offset,
690  // when we apply the transforms matrix in |applyMutators:embeddedView:boundingRect|, we need
691  // to remember to do a reverse translate.
692  const DlRect& rect = params.finalBoundingRect();
693  CGFloat screenScale = [UIScreen mainScreen].scale;
694  clippingView.frame = CGRectMake(rect.GetX() / screenScale, rect.GetY() / screenScale,
695  rect.GetWidth() / screenScale, rect.GetHeight() / screenScale);
696  [self applyMutators:mutatorStack embeddedView:touchInterceptor boundingRect:rect];
697 }
698 
699 - (flutter::DlCanvas*)compositeEmbeddedViewWithId:(int64_t)viewId {
700  FML_DCHECK(self.slices.find(viewId) != self.slices.end());
701  return self.slices[viewId]->canvas();
702 }
703 
704 - (void)reset {
705  // Reset will only be called from the raster thread or a merged raster/platform thread.
706  // _platformViews must only be modified on the platform thread, and any operations that
707  // read or modify platform views should occur there.
708  fml::TaskRunner::RunNowOrPostTask(self.platformTaskRunner, [self]() {
709  for (int64_t viewId : self.compositionOrder) {
710  [self.platformViews[viewId].root_view removeFromSuperview];
711  }
712  self.platformViews.clear();
713  self.previousCompositionOrder.clear();
714  });
715 
716  self.compositionOrder.clear();
717  self.slices.clear();
718  self.currentCompositionParams.clear();
719  self.viewsToRecomposite.clear();
720  self.layerPool->RecycleLayers();
721  self.visitedPlatformViews.clear();
722 }
723 
724 - (BOOL)submitFrame:(std::unique_ptr<flutter::SurfaceFrame>)background_frame
725  withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
726  TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
727 
728  // No platform views to render.
729  if (self.flutterView == nil || (self.compositionOrder.empty() && !self.hadPlatformViews)) {
730  // No platform views to render but the FlutterView may need to be resized.
731  __weak FlutterPlatformViewsController* weakSelf = self;
732  if (self.flutterView != nil) {
733  fml::TaskRunner::RunNowOrPostTask(
734  weakSelf.platformTaskRunner,
735  fml::MakeCopyable([weakSelf, frameSize = weakSelf.frameSize]() {
736  FlutterPlatformViewsController* strongSelf = weakSelf;
737  if (!strongSelf) {
738  return;
739  }
740  [strongSelf performResize:frameSize];
741  }));
742  }
743 
744  self.hadPlatformViews = NO;
745  return background_frame->Submit();
746  }
747  self.hadPlatformViews = !self.compositionOrder.empty();
748 
749  bool didEncode = true;
750  LayersMap platformViewLayers;
751  std::vector<std::unique_ptr<flutter::SurfaceFrame>> surfaceFrames;
752  surfaceFrames.reserve(self.compositionOrder.size());
753  std::unordered_map<int64_t, DlRect> viewRects;
754 
755  for (int64_t viewId : self.compositionOrder) {
756  viewRects[viewId] = self.currentCompositionParams[viewId].finalBoundingRect();
757  }
758 
759  std::unordered_map<int64_t, DlRect> overlayLayers =
760  SliceViews(background_frame->Canvas(), self.compositionOrder, self.slices, viewRects);
761 
762  size_t requiredOverlayLayers = 0;
763  for (int64_t viewId : self.compositionOrder) {
764  std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
765  if (overlay == overlayLayers.end()) {
766  continue;
767  }
768  requiredOverlayLayers++;
769  }
770 
771  // If there are not sufficient overlay layers, we must construct them on the platform
772  // thread, at least until we've refactored iOS surface creation to use IOSurfaces
773  // instead of CALayers.
774  [self createMissingOverlays:requiredOverlayLayers withIosContext:iosContext];
775 
776  int64_t overlayId = 0;
777  for (int64_t viewId : self.compositionOrder) {
778  std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
779  if (overlay == overlayLayers.end()) {
780  continue;
781  }
782  std::shared_ptr<flutter::OverlayLayer> layer = self.nextLayerInPool;
783  if (!layer) {
784  continue;
785  }
786 
787  std::unique_ptr<flutter::SurfaceFrame> frame = layer->surface->AcquireFrame(self.frameSize);
788  // If frame is null, AcquireFrame already printed out an error message.
789  if (!frame) {
790  continue;
791  }
792  flutter::DlCanvas* overlayCanvas = frame->Canvas();
793  int restoreCount = overlayCanvas->GetSaveCount();
794  overlayCanvas->Save();
795  overlayCanvas->ClipRect(overlay->second);
796  overlayCanvas->Clear(flutter::DlColor::kTransparent());
797  self.slices[viewId]->render_into(overlayCanvas);
798  overlayCanvas->RestoreToCount(restoreCount);
799 
800  // This flutter view is never the last in a frame, since we always submit the
801  // underlay view last.
802  frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
803  layer->did_submit_last_frame = frame->Encode();
804 
805  didEncode &= layer->did_submit_last_frame;
806  platformViewLayers[viewId] = LayerData{
807  .rect = overlay->second, //
808  .view_id = viewId, //
809  .overlay_id = overlayId, //
810  .layer = layer //
811  };
812  surfaceFrames.push_back(std::move(frame));
813  overlayId++;
814  }
815 
816  auto previousSubmitInfo = background_frame->submit_info();
817  background_frame->set_submit_info({
818  .frame_damage = previousSubmitInfo.frame_damage,
819  .buffer_damage = previousSubmitInfo.buffer_damage,
820  .present_with_transaction = true,
821  });
822  background_frame->Encode();
823  surfaceFrames.push_back(std::move(background_frame));
824 
825  // Mark all layers as available, so they can be used in the next frame.
826  std::vector<std::shared_ptr<flutter::OverlayLayer>> unusedLayers =
827  self.layerPool->RemoveUnusedLayers();
828  self.layerPool->RecycleLayers();
829  auto task = [self, //
830  platformViewLayers = std::move(platformViewLayers), //
831  currentCompositionParams = self.currentCompositionParams, //
832  viewsToRecomposite = self.viewsToRecomposite, //
833  compositionOrder = self.compositionOrder, //
834  unusedLayers = std::move(unusedLayers), //
835  surfaceFrames = std::move(surfaceFrames)]() mutable {
836  [self performSubmit:platformViewLayers
837  currentCompositionParams:currentCompositionParams
838  viewsToRecomposite:viewsToRecomposite
839  compositionOrder:compositionOrder
840  unusedLayers:unusedLayers
841  surfaceFrames:surfaceFrames];
842  };
843 
844  fml::TaskRunner::RunNowOrPostTask(self.platformTaskRunner, fml::MakeCopyable(std::move(task)));
845  return didEncode;
846 }
847 
848 - (void)createMissingOverlays:(size_t)requiredOverlayLayers
849  withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
850  TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers");
851 
852  if (requiredOverlayLayers <= self.layerPool->size()) {
853  return;
854  }
855  auto missingLayerCount = requiredOverlayLayers - self.layerPool->size();
856 
857  // If the raster thread isn't merged, create layers on the platform thread and block until
858  // complete.
859  auto latch = std::make_shared<fml::CountDownLatch>(1u);
860  fml::TaskRunner::RunNowOrPostTask(
861  self.platformTaskRunner, [self, missingLayerCount, iosContext, latch]() {
862  for (auto i = 0u; i < missingLayerCount; i++) {
863  [self createLayerWithIosContext:iosContext
864  pixelFormat:((FlutterView*)self.flutterView).pixelFormat];
865  }
866  latch->CountDown();
867  });
868  if (![[NSThread currentThread] isMainThread]) {
869  latch->Wait();
870  }
871 }
872 
873 - (void)performResize:(const flutter::DlISize&)frameSize {
874  TRACE_EVENT0("flutter", "PlatformViewsController::PerformResize");
875  FML_DCHECK([[NSThread currentThread] isMainThread]);
876 
877  if (self.flutterView != nil) {
878  [(FlutterView*)self.flutterView
879  setIntrinsicContentSize:CGSizeMake(frameSize.width, frameSize.height)];
880  }
881 }
882 
883 - (void)performSubmit:(const LayersMap&)platformViewLayers
884  currentCompositionParams:
885  (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
886  viewsToRecomposite:(const std::unordered_set<int64_t>&)viewsToRecomposite
887  compositionOrder:(const std::vector<int64_t>&)compositionOrder
888  unusedLayers:
889  (const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
890  surfaceFrames:
891  (const std::vector<std::unique_ptr<flutter::SurfaceFrame>>&)surfaceFrames {
892  TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit");
893  FML_DCHECK([[NSThread currentThread] isMainThread]);
894 
895  [CATransaction begin];
896 
897  // Configure Flutter overlay views.
898  for (const auto& [viewId, layerData] : platformViewLayers) {
899  layerData.layer->UpdateViewState(self.flutterView, //
900  layerData.rect, //
901  layerData.view_id, //
902  layerData.overlay_id //
903  );
904  }
905 
906  // Dispose unused Flutter Views.
907  for (auto& view : [self computeViewsToDispose]) {
908  [view removeFromSuperview];
909  }
910 
911  // Composite Platform Views.
912  for (int64_t viewId : viewsToRecomposite) {
913  [self compositeView:viewId withParams:currentCompositionParams[viewId]];
914  }
915 
916  // Present callbacks.
917  for (const auto& frame : surfaceFrames) {
918  frame->Submit();
919  }
920 
921  // If a layer was allocated in the previous frame, but it's not used in the current frame,
922  // then it can be removed from the scene.
923  [self removeUnusedLayers:unusedLayers withCompositionOrder:compositionOrder];
924 
925  // Organize the layers by their z indexes.
926  [self bringLayersIntoView:platformViewLayers withCompositionOrder:compositionOrder];
927 
928  [CATransaction commit];
929 }
930 
931 - (void)bringLayersIntoView:(const LayersMap&)layerMap
932  withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
933  FML_DCHECK(self.flutterView);
934  UIView* flutterView = self.flutterView;
935 
936  self.previousCompositionOrder.clear();
937  NSMutableArray* desiredPlatformSubviews = [NSMutableArray array];
938  for (int64_t platformViewId : compositionOrder) {
939  self.previousCompositionOrder.push_back(platformViewId);
940  UIView* platformViewRoot = self.platformViews[platformViewId].root_view;
941  if (platformViewRoot != nil) {
942  [desiredPlatformSubviews addObject:platformViewRoot];
943  }
944 
945  auto maybeLayerData = layerMap.find(platformViewId);
946  if (maybeLayerData != layerMap.end()) {
947  auto view = maybeLayerData->second.layer->overlay_view_wrapper;
948  if (view != nil) {
949  [desiredPlatformSubviews addObject:view];
950  }
951  }
952  }
953 
954  NSSet* desiredPlatformSubviewsSet = [NSSet setWithArray:desiredPlatformSubviews];
955  NSArray* existingPlatformSubviews = [flutterView.subviews
956  filteredArrayUsingPredicate:[NSPredicate
957  predicateWithBlock:^BOOL(id object, NSDictionary* bindings) {
958  return [desiredPlatformSubviewsSet containsObject:object];
959  }]];
960 
961  // Manipulate view hierarchy only if needed, to address a performance issue where
962  // this method is called even when view hierarchy stays the same.
963  // See: https://github.com/flutter/flutter/issues/121833
964  // TODO(hellohuanlin): investigate if it is possible to skip unnecessary bringLayersIntoView.
965  if (![desiredPlatformSubviews isEqualToArray:existingPlatformSubviews]) {
966  for (UIView* subview in desiredPlatformSubviews) {
967  // `addSubview` will automatically reorder subview if it is already added.
968  [flutterView addSubview:subview];
969  }
970  }
971 }
972 
973 - (std::shared_ptr<flutter::OverlayLayer>)nextLayerInPool {
974  return self.layerPool->GetNextLayer();
975 }
976 
977 - (void)createLayerWithIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
978  pixelFormat:(MTLPixelFormat)pixelFormat {
979  self.layerPool->CreateLayer(iosContext, pixelFormat);
980 }
981 
982 - (void)removeUnusedLayers:(const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
983  withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
984  for (const std::shared_ptr<flutter::OverlayLayer>& layer : unusedLayers) {
985  [layer->overlay_view_wrapper removeFromSuperview];
986  }
987 
988  std::unordered_set<int64_t> compositionOrderSet;
989  for (int64_t viewId : compositionOrder) {
990  compositionOrderSet.insert(viewId);
991  }
992  // Remove unused platform views.
993  for (int64_t viewId : self.previousCompositionOrder) {
994  if (compositionOrderSet.find(viewId) == compositionOrderSet.end()) {
995  UIView* platformViewRoot = self.platformViews[viewId].root_view;
996  [platformViewRoot removeFromSuperview];
997  }
998  }
999 }
1000 
1001 - (std::vector<UIView*>)computeViewsToDispose {
1002  std::vector<UIView*> views;
1003  if (self.viewsToDispose.empty()) {
1004  return views;
1005  }
1006 
1007  std::unordered_set<int64_t> viewsToComposite(self.compositionOrder.begin(),
1008  self.compositionOrder.end());
1009  std::unordered_set<int64_t> viewsToDelayDispose;
1010  for (int64_t viewId : self.viewsToDispose) {
1011  if (viewsToComposite.count(viewId)) {
1012  viewsToDelayDispose.insert(viewId);
1013  continue;
1014  }
1015  UIView* rootView = self.platformViews[viewId].root_view;
1016  views.push_back(rootView);
1017  self.currentCompositionParams.erase(viewId);
1018  self.viewsToRecomposite.erase(viewId);
1019  self.platformViews.erase(viewId);
1020  }
1021  self.viewsToDispose = std::move(viewsToDelayDispose);
1022  return views;
1023 }
1024 
1025 - (void)resetFrameState {
1026  self.slices.clear();
1027  self.compositionOrder.clear();
1028  self.visitedPlatformViews.clear();
1029 }
1030 
1031 - (void)pushVisitedPlatformViewId:(int64_t)viewId {
1032  self.visitedPlatformViews.push_back(viewId);
1033 }
1034 
1035 - (void)pushClipRectToVisitedPlatformViews:(const flutter::DlRect&)clipRect {
1036  for (int64_t id : self.visitedPlatformViews) {
1037  flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1038  params.PushPlatformViewClipRect(clipRect);
1039  self.currentCompositionParams[id] = params;
1040  }
1041 }
1042 
1043 - (void)pushClipRRectToVisitedPlatformViews:(const flutter::DlRoundRect&)clipRRect {
1044  for (int64_t id : self.visitedPlatformViews) {
1045  flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1046  params.PushPlatformViewClipRRect(clipRRect);
1047  self.currentCompositionParams[id] = params;
1048  }
1049 }
1050 
1051 - (void)pushClipRSuperellipseToVisitedPlatformViews:(const flutter::DlRoundSuperellipse&)clipRse {
1052  for (int64_t id : self.visitedPlatformViews) {
1053  flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1054  params.PushPlatformViewClipRSuperellipse(clipRse);
1055  self.currentCompositionParams[id] = params;
1056  }
1057 }
1058 
1059 - (void)pushClipPathToVisitedPlatformViews:(const flutter::DlPath&)clipPath {
1060  for (int64_t id : self.visitedPlatformViews) {
1061  flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1062  params.PushPlatformViewClipPath(clipPath);
1063  self.currentCompositionParams[id] = params;
1064  }
1065 }
1066 
1067 - (const flutter::EmbeddedViewParams&)compositionParamsForView:(int64_t)viewId {
1068  return self.currentCompositionParams.find(viewId)->second;
1069 }
1070 
1071 #pragma mark - Properties
1072 
1073 - (flutter::OverlayLayerPool*)layerPool {
1074  return _layerPool.get();
1075 }
1076 
1077 - (std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>>&)slices {
1078  return _slices;
1079 }
1080 
1081 - (std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*>&)factories {
1082  return _factories;
1083 }
1084 - (std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>&)
1085  gestureRecognizersBlockingPolicies {
1087 }
1088 
1089 - (std::unordered_map<int64_t, PlatformViewData>&)platformViews {
1090  return _platformViews;
1091 }
1092 
1093 - (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams {
1095 }
1096 
1097 - (std::unordered_set<int64_t>&)viewsToDispose {
1098  return _viewsToDispose;
1099 }
1100 
1101 - (std::vector<int64_t>&)compositionOrder {
1102  return _compositionOrder;
1103 }
1104 
1105 - (std::vector<int64_t>&)visitedPlatformViews {
1106  return _visitedPlatformViews;
1107 }
1108 
1109 - (std::unordered_set<int64_t>&)viewsToRecomposite {
1110  return _viewsToRecomposite;
1111 }
1112 
1113 - (std::vector<int64_t>&)previousCompositionOrder {
1115 }
1116 
1117 @end
void(^ FlutterResult)(id _Nullable result)
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
std::unordered_map< std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy > _gestureRecognizersBlockingPolicies
std::unordered_map< int64_t, LayerData > LayersMap
std::vector< int64_t > _compositionOrder
std::unordered_set< int64_t > _viewsToRecomposite
std::unordered_map< std::string, NSObject< FlutterPlatformViewFactory > * > _factories
std::vector< int64_t > _previousCompositionOrder
static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity
std::unordered_map< int64_t, PlatformViewData > _platformViews
std::unordered_map< int64_t, std::unique_ptr< flutter::EmbedderViewSlice > > _slices
std::vector< int64_t > _visitedPlatformViews
fml::RefPtr< fml::TaskRunner > _platformTaskRunner
std::unordered_map< int64_t, flutter::EmbeddedViewParams > _currentCompositionParams
static void ResetAnchor(CALayer *layer)
static CATransform3D GetCATransform3DFromDlMatrix(const DlMatrix &matrix)
static CGRect GetCGRectFromDlRect(const DlRect &clipDlRect)
std::unordered_set< int64_t > _viewsToDispose
FlutterPlatformViewGestureRecognizersBlockingPolicy
std::vector< int64_t > & previousCompositionOrder()
void applyBlurBackdropFilters:(NSArray< PlatformViewFilter * > *filters)
Storage for Overlay layers across frames.
std::shared_ptr< flutter::OverlayLayer > layer
FlutterTouchInterceptingView * touch_interceptor
NSObject< FlutterPlatformView > * view