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