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