Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
8
19
22using flutter::DlRect;
24
25static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
26
27struct LayerData {
28 DlRect rect;
29 int64_t view_id;
30 int64_t overlay_id;
31 std::shared_ptr<flutter::OverlayLayer> layer;
32};
33using 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.
46static 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.
73static 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
79static 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>
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.dev/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 {
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()) {
514 transformMatrix = transformMatrix * (*iter)->GetMatrix();
515 break;
516 }
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 }
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 }
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 }
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 }
557 embeddedView.alpha = (*iter)->GetAlphaFloat() * embeddedView.alpha;
558 break;
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 if ([pendingClipRRects count] > 0) {
587 cornerRadius = pendingClipRRects[0].topLeftRadius;
588 [pendingClipRRects removeAllObjects];
589 }
590 visualEffectView.layer.cornerRadius = cornerRadius;
591 visualEffectView.clipsToBounds = YES;
592
593 PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView
594 blurRadius:blurRadius
595 cornerRadius:cornerRadius
596 visualEffectView:visualEffectView];
597 if (!filter) {
598 self.canApplyBlurBackdrop = NO;
599 } else {
600 [blurFilters addObject:filter];
601 }
602 break;
603 }
605 // The frame already handles cropping into the rect so this can
606 // no-op
607 break;
608 }
610 PendingRRectClip* clip = [[PendingRRectClip alloc] init];
611 DlRoundRect rrect = (*iter)->GetBackdropClipRRect().rrect;
612
613 clip.rect = boundingRect;
614 impeller::RoundingRadii radii = rrect.GetRadii();
615 clip.topLeftRadius = radii.top_left.width;
616 clip.topRightRadius = radii.top_right.width;
619 [pendingClipRRects addObject:clip];
620 break;
621 }
623 // TODO(https://github.com/flutter/flutter/issues/179125)
624 break;
625 }
627 // TODO(https://github.com/flutter/flutter/issues/179127)
628 break;
629 }
630 }
631 ++iter;
632 }
633
634 if (self.canApplyBlurBackdrop) {
635 [clipView applyBlurBackdropFilters:blurFilters];
636 }
637
638 // The UIKit frame is set based on the logical resolution (points) instead of physical.
639 // (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
640 // However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
641 // 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
642 // down to the logical resoltion before applying it to the layer of PlatformView.
643 flutter::DlScalar pointScale = 1.0 / screenScale;
644 transformMatrix = DlMatrix::MakeScale({pointScale, pointScale, 1}) * transformMatrix;
645
646 // Reverse the offset of the clipView.
647 // The clipView's frame includes the final translate of the final transform matrix.
648 // Thus, this translate needs to be reversed so the platform view can layout at the correct
649 // offset.
650 //
651 // Note that the transforms are not applied to the clipping paths because clipping paths happen on
652 // the mask view, whose origin is always (0,0) to the _flutterView.
653 impeller::Vector3 origin = impeller::Vector3(clipView.frame.origin.x, clipView.frame.origin.y);
654 transformMatrix = DlMatrix::MakeTranslation(-origin) * transformMatrix;
655
656 embeddedView.layer.transform = GetCATransform3DFromDlMatrix(transformMatrix);
657}
658
659- (void)compositeView:(int64_t)viewId withParams:(const flutter::EmbeddedViewParams&)params {
660 // TODO(https://github.com/flutter/flutter/issues/109700)
661 CGRect frame = CGRectMake(0, 0, params.sizePoints().width, params.sizePoints().height);
662 FlutterTouchInterceptingView* touchInterceptor = self.platformViews[viewId].touch_interceptor;
663 touchInterceptor.layer.transform = CATransform3DIdentity;
664 touchInterceptor.frame = frame;
665 touchInterceptor.alpha = 1;
666
667 const flutter::MutatorsStack& mutatorStack = params.mutatorsStack();
668 UIView* clippingView = self.platformViews[viewId].root_view;
669 // The frame of the clipping view should be the final bounding rect.
670 // Because the translate matrix in the Mutator Stack also includes the offset,
671 // when we apply the transforms matrix in |applyMutators:embeddedView:boundingRect|, we need
672 // to remember to do a reverse translate.
673 const DlRect& rect = params.finalBoundingRect();
674 CGFloat screenScale = [UIScreen mainScreen].scale;
675 clippingView.frame = CGRectMake(rect.GetX() / screenScale, rect.GetY() / screenScale,
676 rect.GetWidth() / screenScale, rect.GetHeight() / screenScale);
677 [self applyMutators:mutatorStack embeddedView:touchInterceptor boundingRect:rect];
678}
679
680- (flutter::DlCanvas*)compositeEmbeddedViewWithId:(int64_t)viewId {
681 FML_DCHECK(self.slices.find(viewId) != self.slices.end());
682 return self.slices[viewId]->canvas();
683}
684
685- (void)reset {
686 // Reset will only be called from the raster thread or a merged raster/platform thread.
687 // _platformViews must only be modified on the platform thread, and any operations that
688 // read or modify platform views should occur there.
689 fml::TaskRunner::RunNowOrPostTask(self.platformTaskRunner, [self]() {
690 for (int64_t viewId : self.compositionOrder) {
691 [self.platformViews[viewId].root_view removeFromSuperview];
692 }
693 self.platformViews.clear();
694 self.previousCompositionOrder.clear();
695 });
696
697 self.compositionOrder.clear();
698 self.slices.clear();
699 self.currentCompositionParams.clear();
700 self.viewsToRecomposite.clear();
701 self.layerPool->RecycleLayers();
702 self.visitedPlatformViews.clear();
703}
704
705- (BOOL)submitFrame:(std::unique_ptr<flutter::SurfaceFrame>)background_frame
706 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
707 TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
708
709 // No platform views to render.
710 if (self.flutterView == nil || (self.compositionOrder.empty() && !self.hadPlatformViews)) {
711 // No platform views to render but the FlutterView may need to be resized.
712 __weak FlutterPlatformViewsController* weakSelf = self;
713 if (self.flutterView != nil) {
715 weakSelf.platformTaskRunner,
716 fml::MakeCopyable([weakSelf, frameSize = weakSelf.frameSize]() {
717 FlutterPlatformViewsController* strongSelf = weakSelf;
718 if (!strongSelf) {
719 return;
720 }
721 [strongSelf performResize:frameSize];
722 }));
723 }
724
725 self.hadPlatformViews = NO;
726 return background_frame->Submit();
727 }
728 self.hadPlatformViews = !self.compositionOrder.empty();
729
730 bool didEncode = true;
731 LayersMap platformViewLayers;
732 std::vector<std::unique_ptr<flutter::SurfaceFrame>> surfaceFrames;
733 surfaceFrames.reserve(self.compositionOrder.size());
734 std::unordered_map<int64_t, DlRect> viewRects;
735
736 for (int64_t viewId : self.compositionOrder) {
737 viewRects[viewId] = self.currentCompositionParams[viewId].finalBoundingRect();
738 }
739
740 std::unordered_map<int64_t, DlRect> overlayLayers =
741 SliceViews(background_frame->Canvas(), self.compositionOrder, self.slices, viewRects);
742
743 size_t requiredOverlayLayers = 0;
744 for (int64_t viewId : self.compositionOrder) {
745 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
746 if (overlay == overlayLayers.end()) {
747 continue;
748 }
749 requiredOverlayLayers++;
750 }
751
752 // If there are not sufficient overlay layers, we must construct them on the platform
753 // thread, at least until we've refactored iOS surface creation to use IOSurfaces
754 // instead of CALayers.
755 [self createMissingOverlays:requiredOverlayLayers withIosContext:iosContext];
756
757 int64_t overlayId = 0;
758 for (int64_t viewId : self.compositionOrder) {
759 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
760 if (overlay == overlayLayers.end()) {
761 continue;
762 }
763 std::shared_ptr<flutter::OverlayLayer> layer = self.nextLayerInPool;
764 if (!layer) {
765 continue;
766 }
767
768 std::unique_ptr<flutter::SurfaceFrame> frame = layer->surface->AcquireFrame(self.frameSize);
769 // If frame is null, AcquireFrame already printed out an error message.
770 if (!frame) {
771 continue;
772 }
773 flutter::DlCanvas* overlayCanvas = frame->Canvas();
774 int restoreCount = overlayCanvas->GetSaveCount();
775 overlayCanvas->Save();
776 overlayCanvas->ClipRect(overlay->second);
777 overlayCanvas->Clear(flutter::DlColor::kTransparent());
778 self.slices[viewId]->render_into(overlayCanvas);
779 overlayCanvas->RestoreToCount(restoreCount);
780
781 // This flutter view is never the last in a frame, since we always submit the
782 // underlay view last.
783 frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
784 layer->did_submit_last_frame = frame->Encode();
785
786 didEncode &= layer->did_submit_last_frame;
787 platformViewLayers[viewId] = LayerData{
788 .rect = overlay->second, //
789 .view_id = viewId, //
790 .overlay_id = overlayId, //
791 .layer = layer //
792 };
793 surfaceFrames.push_back(std::move(frame));
794 overlayId++;
795 }
796
797 auto previousSubmitInfo = background_frame->submit_info();
798 background_frame->set_submit_info({
799 .frame_damage = previousSubmitInfo.frame_damage,
800 .buffer_damage = previousSubmitInfo.buffer_damage,
801 .present_with_transaction = true,
802 });
803 background_frame->Encode();
804 surfaceFrames.push_back(std::move(background_frame));
805
806 // Mark all layers as available, so they can be used in the next frame.
807 std::vector<std::shared_ptr<flutter::OverlayLayer>> unusedLayers =
808 self.layerPool->RemoveUnusedLayers();
809 self.layerPool->RecycleLayers();
810 auto task = [self, //
811 platformViewLayers = std::move(platformViewLayers), //
812 currentCompositionParams = self.currentCompositionParams, //
813 viewsToRecomposite = self.viewsToRecomposite, //
814 compositionOrder = self.compositionOrder, //
815 unusedLayers = std::move(unusedLayers), //
816 surfaceFrames = std::move(surfaceFrames)]() mutable {
817 [self performSubmit:platformViewLayers
818 currentCompositionParams:currentCompositionParams
819 viewsToRecomposite:viewsToRecomposite
820 compositionOrder:compositionOrder
821 unusedLayers:unusedLayers
822 surfaceFrames:surfaceFrames];
823 };
824
825 fml::TaskRunner::RunNowOrPostTask(self.platformTaskRunner, fml::MakeCopyable(std::move(task)));
826 return didEncode;
827}
828
829- (void)createMissingOverlays:(size_t)requiredOverlayLayers
830 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
831 TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers");
832
833 if (requiredOverlayLayers <= self.layerPool->size()) {
834 return;
835 }
836 auto missingLayerCount = requiredOverlayLayers - self.layerPool->size();
837
838 // If the raster thread isn't merged, create layers on the platform thread and block until
839 // complete.
840 auto latch = std::make_shared<fml::CountDownLatch>(1u);
842 self.platformTaskRunner, [self, missingLayerCount, iosContext, latch]() {
843 for (auto i = 0u; i < missingLayerCount; i++) {
844 [self createLayerWithIosContext:iosContext
845 pixelFormat:((FlutterView*)self.flutterView).pixelFormat];
846 }
847 latch->CountDown();
848 });
849 if (![[NSThread currentThread] isMainThread]) {
850 latch->Wait();
851 }
852}
853
854- (void)performResize:(const flutter::DlISize&)frameSize {
855 TRACE_EVENT0("flutter", "PlatformViewsController::PerformResize");
856 FML_DCHECK([[NSThread currentThread] isMainThread]);
857
858 if (self.flutterView != nil) {
859 [(FlutterView*)self.flutterView
860 setIntrinsicContentSize:CGSizeMake(frameSize.width, frameSize.height)];
861 }
862}
863
864- (void)performSubmit:(const LayersMap&)platformViewLayers
865 currentCompositionParams:
866 (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
867 viewsToRecomposite:(const std::unordered_set<int64_t>&)viewsToRecomposite
868 compositionOrder:(const std::vector<int64_t>&)compositionOrder
869 unusedLayers:
870 (const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
871 surfaceFrames:
872 (const std::vector<std::unique_ptr<flutter::SurfaceFrame>>&)surfaceFrames {
873 TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit");
874 FML_DCHECK([[NSThread currentThread] isMainThread]);
875
876 [CATransaction begin];
877
878 // Configure Flutter overlay views.
879 for (const auto& [viewId, layerData] : platformViewLayers) {
880 layerData.layer->UpdateViewState(self.flutterView, //
881 layerData.rect, //
882 layerData.view_id, //
883 layerData.overlay_id //
884 );
885 }
886
887 // Dispose unused Flutter Views.
888 for (auto& view : [self computeViewsToDispose]) {
889 [view removeFromSuperview];
890 }
891
892 // Composite Platform Views.
893 for (int64_t viewId : viewsToRecomposite) {
894 [self compositeView:viewId withParams:currentCompositionParams[viewId]];
895 }
896
897 // Present callbacks.
898 for (const auto& frame : surfaceFrames) {
899 frame->Submit();
900 }
901
902 // If a layer was allocated in the previous frame, but it's not used in the current frame,
903 // then it can be removed from the scene.
904 [self removeUnusedLayers:unusedLayers withCompositionOrder:compositionOrder];
905
906 // Organize the layers by their z indexes.
907 [self bringLayersIntoView:platformViewLayers withCompositionOrder:compositionOrder];
908
909 [CATransaction commit];
910}
911
912- (void)bringLayersIntoView:(const LayersMap&)layerMap
913 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
914 FML_DCHECK(self.flutterView);
915 UIView* flutterView = self.flutterView;
916
917 self.previousCompositionOrder.clear();
918 NSMutableArray* desiredPlatformSubviews = [NSMutableArray array];
919 for (int64_t platformViewId : compositionOrder) {
920 self.previousCompositionOrder.push_back(platformViewId);
921 UIView* platformViewRoot = self.platformViews[platformViewId].root_view;
922 if (platformViewRoot != nil) {
923 [desiredPlatformSubviews addObject:platformViewRoot];
924 }
925
926 auto maybeLayerData = layerMap.find(platformViewId);
927 if (maybeLayerData != layerMap.end()) {
928 auto view = maybeLayerData->second.layer->overlay_view_wrapper;
929 if (view != nil) {
930 [desiredPlatformSubviews addObject:view];
931 }
932 }
933 }
934
935 NSSet* desiredPlatformSubviewsSet = [NSSet setWithArray:desiredPlatformSubviews];
936 NSArray* existingPlatformSubviews = [flutterView.subviews
937 filteredArrayUsingPredicate:[NSPredicate
938 predicateWithBlock:^BOOL(id object, NSDictionary* bindings) {
939 return [desiredPlatformSubviewsSet containsObject:object];
940 }]];
941
942 // Manipulate view hierarchy only if needed, to address a performance issue where
943 // this method is called even when view hierarchy stays the same.
944 // See: https://github.com/flutter/flutter/issues/121833
945 // TODO(hellohuanlin): investigate if it is possible to skip unnecessary bringLayersIntoView.
946 if (![desiredPlatformSubviews isEqualToArray:existingPlatformSubviews]) {
947 for (UIView* subview in desiredPlatformSubviews) {
948 // `addSubview` will automatically reorder subview if it is already added.
949 [flutterView addSubview:subview];
950 }
951 }
952}
953
954- (std::shared_ptr<flutter::OverlayLayer>)nextLayerInPool {
955 return self.layerPool->GetNextLayer();
956}
957
958- (void)createLayerWithIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
959 pixelFormat:(MTLPixelFormat)pixelFormat {
960 self.layerPool->CreateLayer(iosContext, pixelFormat);
961}
962
963- (void)removeUnusedLayers:(const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
964 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
965 for (const std::shared_ptr<flutter::OverlayLayer>& layer : unusedLayers) {
966 [layer->overlay_view_wrapper removeFromSuperview];
967 }
968
969 std::unordered_set<int64_t> compositionOrderSet;
970 for (int64_t viewId : compositionOrder) {
971 compositionOrderSet.insert(viewId);
972 }
973 // Remove unused platform views.
974 for (int64_t viewId : self.previousCompositionOrder) {
975 if (compositionOrderSet.find(viewId) == compositionOrderSet.end()) {
976 UIView* platformViewRoot = self.platformViews[viewId].root_view;
977 [platformViewRoot removeFromSuperview];
978 }
979 }
980}
981
982- (std::vector<UIView*>)computeViewsToDispose {
983 std::vector<UIView*> views;
984 if (self.viewsToDispose.empty()) {
985 return views;
986 }
987
988 std::unordered_set<int64_t> viewsToComposite(self.compositionOrder.begin(),
989 self.compositionOrder.end());
990 std::unordered_set<int64_t> viewsToDelayDispose;
991 for (int64_t viewId : self.viewsToDispose) {
992 if (viewsToComposite.count(viewId)) {
993 viewsToDelayDispose.insert(viewId);
994 continue;
995 }
996 UIView* rootView = self.platformViews[viewId].root_view;
997 views.push_back(rootView);
998 self.currentCompositionParams.erase(viewId);
999 self.viewsToRecomposite.erase(viewId);
1000 self.platformViews.erase(viewId);
1001 }
1002 self.viewsToDispose = std::move(viewsToDelayDispose);
1003 return views;
1004}
1005
1006- (void)resetFrameState {
1007 self.slices.clear();
1008 self.compositionOrder.clear();
1009 self.visitedPlatformViews.clear();
1010}
1011
1012- (void)pushVisitedPlatformViewId:(int64_t)viewId {
1013 self.visitedPlatformViews.push_back(viewId);
1014}
1015
1016- (void)pushClipRectToVisitedPlatformViews:(const flutter::DlRect&)clipRect {
1017 for (int64_t id : self.visitedPlatformViews) {
1018 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1020 self.currentCompositionParams[id] = params;
1021 }
1022}
1023
1024- (void)pushClipRRectToVisitedPlatformViews:(const flutter::DlRoundRect&)clipRRect {
1025 for (int64_t id : self.visitedPlatformViews) {
1026 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1028 self.currentCompositionParams[id] = params;
1029 }
1030}
1031
1032- (void)pushClipRSuperellipseToVisitedPlatformViews:(const flutter::DlRoundSuperellipse&)clipRse {
1033 for (int64_t id : self.visitedPlatformViews) {
1034 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1036 self.currentCompositionParams[id] = params;
1037 }
1038}
1039
1040- (void)pushClipPathToVisitedPlatformViews:(const flutter::DlPath&)clipPath {
1041 for (int64_t id : self.visitedPlatformViews) {
1042 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1044 self.currentCompositionParams[id] = params;
1045 }
1046}
1047
1048- (const flutter::EmbeddedViewParams&)compositionParamsForView:(int64_t)viewId {
1049 return self.currentCompositionParams.find(viewId)->second;
1050}
1051
1052#pragma mark - Properties
1053
1054- (flutter::OverlayLayerPool*)layerPool {
1055 return _layerPool.get();
1056}
1057
1058- (std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>>&)slices {
1059 return _slices;
1060}
1061
1062- (std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*>&)factories {
1063 return _factories;
1064}
1065- (std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>&)
1066 gestureRecognizersBlockingPolicies {
1068}
1069
1070- (std::unordered_map<int64_t, PlatformViewData>&)platformViews {
1071 return _platformViews;
1072}
1073
1074- (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams {
1076}
1077
1078- (std::unordered_set<int64_t>&)viewsToDispose {
1079 return _viewsToDispose;
1080}
1081
1082- (std::vector<int64_t>&)compositionOrder {
1083 return _compositionOrder;
1084}
1085
1086- (std::vector<int64_t>&)visitedPlatformViews {
1087 return _visitedPlatformViews;
1088}
1089
1090- (std::unordered_set<int64_t>&)viewsToRecomposite {
1091 return _viewsToRecomposite;
1092}
1093
1094- (std::vector<int64_t>&)previousCompositionOrder {
1096}
1097
1098@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
static bool TransformedRectCoversBounds(const DlRect &local_rect, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local rect, when transformed by the matrix, completely covers the indicated culling bou...
static bool TransformedRoundSuperellipseCoversBounds(const DlRoundSuperellipse &local_rse, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local round superellipse, when transformed by the matrix, completely covers the indicat...
static bool TransformedRRectCoversBounds(const DlRoundRect &local_rrect, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local round rect, when transformed by the matrix, completely covers the indicated culli...
Developer-facing API for rendering anything within the engine.
Definition dl_canvas.h:32
virtual void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)=0
virtual int GetSaveCount() const =0
virtual void RestoreToCount(int restore_count)=0
void Clear(DlColor color)
Definition dl_canvas.h:104
virtual void Save()=0
void PushImageFilter(const std::shared_ptr< DlImageFilter > &filter, const DlRect &filter_rect)
void PushPlatformViewClipRRect(const DlRoundRect &clip_rrect)
void PushPlatformViewClipPath(const DlPath &clip_path)
void PushPlatformViewClipRSuperellipse(const DlRoundSuperellipse &clip_rse)
void PushPlatformViewClipRect(const DlRect &clip_rect)
Storage for Overlay layers across frames.
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
const EmbeddedViewParams * params
FlView * view
const char * message
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
HWND(* FlutterPlatformViewFactory)(const FlutterPlatformViewCreationParameters *)
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
impeller::Scalar DlScalar
std::unordered_map< int64_t, DlRect > SliceViews(DlCanvas *background_canvas, const std::vector< int64_t > &composition_order, const std::unordered_map< int64_t, std::unique_ptr< EmbedderViewSlice > > &slices, const std::unordered_map< int64_t, DlRect > &view_rects)
Compute the required overlay layers and clip the view slices according to the size and position of th...
impeller::RoundRect DlRoundRect
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::ISize32 DlISize
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
impeller::RoundSuperellipse DlRoundSuperellipse
internal::CopyableLambda< T > MakeCopyable(T lambda)
flutter::DlPath DlPath
Definition ref_ptr.h:261
std::shared_ptr< flutter::OverlayLayer > layer
FlutterTouchInterceptingView * touch_interceptor
NSObject< FlutterPlatformView > * view
static constexpr DlColor kTransparent()
Definition dl_color.h:68
A 4x4 matrix using column-major storage.
Definition matrix.h:37
Scalar m[16]
Definition matrix.h:39
Type width
Definition size.h:28
const uintptr_t id
#define TRACE_EVENT0(category_group, name)
int BOOL