7#import <WebKit/WebKit.h>
17 return CGRectMake(clipDlRect.
GetX(),
24 CATransform3D
transform = CATransform3DIdentity;
50 CGPathMoveToPoint(path_ref_, nil,
p2.x,
p2.y);
53 CGPathAddLineToPoint(path_ref_, nil,
p2.x,
p2.y);
56 CGPathAddQuadCurveToPoint(path_ref_, nil, cp.
x, cp.
y,
p2.x,
p2.y);
62 CGPathAddCurveToPoint(path_ref_, nil,
63 cp1.
x, cp1.
y, cp2.
x, cp2.
y,
p2.x,
p2.y);
65 void Close()
override { CGPathCloseSubpath(path_ref_); }
67 CGMutablePathRef
TakePath()
const {
return path_ref_; }
70 CGMutablePathRef path_ref_ = CGPathCreateMutable();
77@property(nonatomic)
BOOL backdropFilterViewConfigured;
82- (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView;
96 blurRadius:(CGFloat)blurRadius
97 cornerRadius:(CGFloat)cornerRadius
98 isRoundedSuperellipse:(
BOOL)isRoundedSuperellipse
99 visualEffectView:(UIVisualEffectView*)visualEffectView {
100 if (
self = [super init]) {
107 FML_DLOG(ERROR) <<
"Apple's API for UIVisualEffectView changed. Update the implementation to "
108 "access the gaussianBlur CAFilter.";
111 _backdropFilterView = visualEffectView;
112 _backdropFilterViewConfigured = NO;
124+ (void)prepareOnce:(UIVisualEffectView*)visualEffectView {
128 for (NSUInteger
i = 0;
i < visualEffectView.subviews.count;
i++) {
129 UIView*
view = visualEffectView.subviews[i];
130 if ([NSStringFromClass([view
class]) hasSuffix:
@"BackdropView"]) {
132 for (NSObject* filter in
view.layer.filters) {
133 if ([[filter valueForKey:
@"name"] isEqual:
@"gaussianBlur"] &&
134 [[filter valueForKey:
@"inputRadius"] isKindOfClass:[NSNumber class]]) {
139 }
else if ([NSStringFromClass([view
class]) hasSuffix:
@"VisualEffectSubview"]) {
146+ (
BOOL)isUIVisualEffectViewImplementationValid {
152 if (!
self.backdropFilterViewConfigured) {
153 [
self updateVisualEffectView:_backdropFilterView];
154 self.backdropFilterViewConfigured = YES;
156 return _backdropFilterView;
159- (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView {
160 NSObject* gaussianBlurFilter = [_gaussianBlurFilter copy];
162 UIView* backdropView = visualEffectView.subviews[_indexOfBackdropView];
163 [gaussianBlurFilter setValue:@(_blurRadius) forKey:@"inputRadius"];
164 backdropView.layer.filters = @[ gaussianBlurFilter ];
166 UIView* visualEffectSubview = visualEffectView.subviews[_indexOfVisualEffectSubview];
167 visualEffectSubview.layer.backgroundColor = UIColor.clearColor.CGColor;
168 visualEffectView.frame = _frame;
170 visualEffectView.layer.cornerRadius = _cornerRadius;
171 visualEffectView.layer.cornerCurve =
172 _isRoundedSuperellipse ? kCACornerCurveContinuous : kCACornerCurveCircular;
173 visualEffectView.clipsToBounds = YES;
175 self.backdropFilterView = visualEffectView;
182@property(nonatomic, copy) NSArray<PlatformViewFilter*>* filters;
192- (
BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
193 for (UIView*
view in
self.subviews) {
194 if ([
view pointInside:[
self convertPoint:point toView:view] withEvent:event]) {
203 if (
self.filters.count == 0 && filters.count == 0) {
206 self.filters = filters;
207 NSUInteger index = 0;
208 for (index = 0; index <
self.filters.count; index++) {
209 UIVisualEffectView* backdropFilterView;
211 if (
self.backdropFilterSubviews.count <= index) {
212 backdropFilterView = filter.backdropFilterView;
213 [self addSubview:backdropFilterView];
214 [self.backdropFilterSubviews addObject:backdropFilterView];
216 [filter updateVisualEffectView:self.backdropFilterSubviews[index]];
219 for (NSUInteger
i =
self.backdropFilterSubviews.count;
i > index;
i--) {
220 [
self.backdropFilterSubviews[i - 1] removeFromSuperview];
221 [
self.backdropFilterSubviews removeLastObject];
226 if (!_backdropFilterSubviews) {
227 _backdropFilterSubviews = [[NSMutableArray alloc] init];
229 return _backdropFilterSubviews;
244@property(nonatomic) CATransform3D reverseScreenScale;
246- (
fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
251 std::vector<fml::CFRef<CGPathRef>> paths_;
257 return [
self initWithFrame:frame screenScale:[UIScreen mainScreen].scale];
260- (instancetype)
initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale {
262 self.backgroundColor = UIColor.clearColor;
263 _reverseScreenScale = CATransform3DMakeScale(1 / screenScale, 1 / screenScale, 1);
271 return [CAShapeLayer class];
274- (CAShapeLayer*)shapeLayer {
275 return (CAShapeLayer*)
self.layer;
282 [
self shapeLayer].path = nil;
283 [
self setNeedsDisplay];
291- (
BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
295- (void)drawRect:(CGRect)rect {
299 CGContextRef
context = UIGraphicsGetCurrentContext();
305 for (
size_t i = 0;
i < paths_.size();
i++) {
306 CGContextAddPath(
context, paths_.at(
i));
309 CGContextFillRect(
context, rect);
310 CGContextRestoreGState(
context);
314 [
super drawRect:rect];
315 if (![
self shapeLayer].path) {
316 if (paths_.size() == 1) {
318 [
self shapeLayer].path = paths_.at(0);
321 CGPathRef pathSoFar = CGPathCreateWithRect(
rectSoFar_, nil);
322 [
self shapeLayer].path = pathSoFar;
323 CGPathRelease(pathSoFar);
331 CGPathRef
path = CGPathCreateWithRect(clipRect, nil);
333 CATransform3D matrixInPoints =
335 paths_.push_back([
self getTransformedPath:
path matrix:matrixInPoints]);
336 CGAffineTransform affine = [
self affineWithMatrix:matrixInPoints];
338 if (affine.b == 0 && affine.c == 0) {
346 if (clipDlRRect.IsEmpty()) {
348 }
else if (clipDlRRect.IsRect()) {
349 [
self clipRect:clipDlRRect.GetBounds() matrix:matrix];
352 CGPathRef pathRef =
nullptr;
355 if (clipDlRRect.GetRadii().AreAllCornersSame()) {
357 auto radii = clipDlRRect.GetRadii();
359 CGPathCreateWithRoundedRect(clipRect, radii.top_left.width, radii.top_left.height, nil);
361 CGMutablePathRef mutablePathRef = CGPathCreateMutable();
365 auto top = clipDlRect.
GetTop();
379 CGPathMoveToPoint(mutablePathRef, nil,
380 left + top_left.width, top);
382 CGPathAddLineToPoint(mutablePathRef, nil,
383 right - top_right.width, top);
384 CGPathAddCurveToPoint(mutablePathRef, nil,
386 right, top + top_right.height,
387 right, top + top_right.height);
389 CGPathAddLineToPoint(mutablePathRef, nil,
390 right, bottom - bottom_right.height);
391 CGPathAddCurveToPoint(mutablePathRef, nil,
393 right - bottom_right.width, bottom,
394 right - bottom_right.width, bottom);
396 CGPathAddLineToPoint(mutablePathRef, nil,
397 left + bottom_left.width, bottom);
398 CGPathAddCurveToPoint(mutablePathRef, nil,
400 left, bottom - bottom_left.height,
401 left, bottom - bottom_left.height);
403 CGPathAddLineToPoint(mutablePathRef, nil,
404 left, top + top_left.height);
405 CGPathAddCurveToPoint(mutablePathRef, nil,
407 left + top_left.width, top,
408 left + top_left.width, top);
409 CGPathCloseSubpath(mutablePathRef);
410 pathRef = mutablePathRef;
413 CATransform3D matrixInPoints =
418 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
425 CGPathReceiver receiver;
430 dlPath.Dispatch(receiver);
433 CATransform3D matrixInPoints =
435 paths_.push_back([
self getTransformedPath:receiver.TakePath() matrix:matrixInPoints]);
438- (CGAffineTransform)affineWithMatrix:(CATransform3D)matrix {
439 return CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41,
443- (
fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
444 CGAffineTransform affine = [
self affineWithMatrix:matrix];
445 CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine);
457@property(nonatomic) NSUInteger capacity;
461@property(nonatomic) NSMutableSet<FlutterClippingMaskView*>* pool;
467- (instancetype)initWithCapacity:(NSInteger)capacity {
468 if (
self = [super init]) {
471 _pool = [[NSMutableSet alloc] initWithCapacity:1];
472 _capacity = capacity;
479 if (
self.pool.count == 0) {
482 screenScale:UIScreen.mainScreen.scale];
485 maskView.frame = frame;
487 [
self.pool removeObject:maskView];
494 if (
self.pool.count ==
self.capacity) {
497 [
self.pool addObject:maskView];
502@implementation UIView (FirstResponder)
504 if (
self.isFirstResponder) {
507 for (UIView* subview in
self.subviews) {
508 if (subview.flt_hasFirstResponderInViewHierarchySubtree) {
517@property(nonatomic, weak, readonly) UIView* embeddedView;
518@property(nonatomic, weak, readonly) UIViewController<FlutterViewResponder>* flutterViewController;
524- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
526 gestureRecognizersBlockingPolicy:
528 self = [
super initWithFrame:embeddedView.frame];
530 self.multipleTouchEnabled = YES;
531 _embeddedView = embeddedView;
532 _platformViewsController = platformViewsController;
534 embeddedView.autoresizingMask =
535 (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
537 [
self addSubview:embeddedView];
541 platformViewsController:platformViewsController];
543 _delayingRecognizer =
546 forwardingRecognizer:forwardingRecognizer];
547 _blockingPolicy = blockingPolicy;
553 [
self addGestureRecognizer:_delayingRecognizer];
555 [
self addGestureRecognizer:forwardingRecognizer];
560- (void)forceResetForwardingGestureRecognizerState {
568 [oldForwardingRecognizer recreateRecognizerWithTarget:self];
569 self.delayingRecognizer.forwardingRecognizer = newForwardingRecognizer;
570 [
self removeGestureRecognizer:oldForwardingRecognizer];
571 [
self addGestureRecognizer:newForwardingRecognizer];
574- (void)releaseGesture {
575 self.delayingRecognizer.state = UIGestureRecognizerStateFailed;
578- (
BOOL)containsWebView:(UIView*)view {
579 if ([view isKindOfClass:[WKWebView class]]) {
582 for (UIView* subview in
view.subviews) {
583 if ([
self containsWebView:subview]) {
590- (void)searchAndFixWebView:(UIView*)view {
591 if ([view isKindOfClass:[WKWebView class]]) {
592 return [
self searchAndFixWebViewGestureRecognzier:view];
594 for (UIView* subview in
view.subviews) {
595 [
self searchAndFixWebView:subview];
600- (void)searchAndFixWebViewGestureRecognzier:(UIView*)view {
601 for (UIGestureRecognizer* recognizer in
view.gestureRecognizers) {
612 if (recognizer.enabled &&
613 [NSStringFromClass([recognizer
class]) hasSuffix:
@"TouchEventsGestureRecognizer"]) {
614 recognizer.enabled = NO;
615 recognizer.enabled = YES;
618 for (UIView* subview in
view.subviews) {
619 [
self searchAndFixWebViewGestureRecognzier:subview];
623- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
626 if (
self.flutterViewController == nil) {
629 CGPoint pointInFlutterView = [
self convertPoint:point toView:self.flutterViewController.view];
634 if (![
self.flutterViewController
635 platformViewShouldAcceptTouchAtTouchBeganLocation:pointInFlutterView]) {
639 return [
super hitTest:point withEvent:event];
642- (void)blockGesture {
643 switch (_blockingPolicy) {
649 self.delayingRecognizer.state = UIGestureRecognizerStateEnded;
659 if (@available(iOS 26.4, *)) {
662 }
else if (@available(iOS 26.0, *)) {
667 NSNumber* isWorkaroundDisabled =
668 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisableWebViewGestureReset"];
669 if (!isWorkaroundDisabled.boolValue) {
670 [
self searchAndFixWebView:self.embeddedView];
672 }
else if (@available(iOS 18.2, *)) {
676 if ([
self containsWebView:
self.embeddedView]) {
677 [
self removeGestureRecognizer:self.delayingRecognizer];
678 [
self addGestureRecognizer:self.delayingRecognizer];
684 if (
self.delayingRecognizer.touchedEndedWithoutBlocking) {
688 self.delayingRecognizer.state = UIGestureRecognizerStateEnded;
693 self.delayingRecognizer.shouldEndInNextTouchesEnded = YES;
704- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
707- (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
710- (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
713- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
716- (
id)accessibilityContainer {
717 return self.flutterAccessibilityContainer;
724- (instancetype)initWithTarget:(
id)target
726 forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer {
727 self = [
super initWithTarget:target action:action];
729 self.delaysTouchesBegan = YES;
730 self.delaysTouchesEnded = YES;
732 _shouldEndInNextTouchesEnded = NO;
733 _touchedEndedWithoutBlocking = NO;
734 _forwardingRecognizer = forwardingRecognizer;
739- (
BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
740 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
743 return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer !=
self;
746- (
BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
747 shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
748 return otherGestureRecognizer ==
self;
751- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
753 [
super touchesBegan:touches withEvent:event];
756- (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
757 if (
self.shouldEndInNextTouchesEnded) {
758 self.state = UIGestureRecognizerStateEnded;
759 self.shouldEndInNextTouchesEnded = NO;
761 self.touchedEndedWithoutBlocking = YES;
763 [
super touchesEnded:touches withEvent:event];
766- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
767 self.state = UIGestureRecognizerStateFailed;
789- (instancetype)initWithTarget:(
id)target
791 self = [
super initWithTarget:target action:nil];
795 _platformViewsController = platformViewsController;
803 platformViewsController:_platformViewsController];
806- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
821 [_flutterViewController touchesBegan:touches withEvent:event];
825- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
826 [_flutterViewController touchesMoved:touches withEvent:event];
829- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
830 [_flutterViewController touchesEnded:touches withEvent:event];
837 self.state = UIGestureRecognizerStateFailed;
839 [
self forceResetStateIfNeeded];
843- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
849 [_flutterViewController forceTouchesCancelled:touches];
852 self.state = UIGestureRecognizerStateFailed;
854 [
self forceResetStateIfNeeded];
858- (void)forceResetStateIfNeeded {
862 if (@available(iOS 26.0, *)) {
866 dispatch_async(dispatch_get_main_queue(), ^{
871 if (strongSelf.state != UIGestureRecognizerStatePossible) {
872 [(FlutterTouchInterceptingView*)strongSelf.view forceResetForwardingGestureRecognizerState];
877- (
BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
878 shouldRecognizeSimultaneouslyWithGestureRecognizer:
879 (UIGestureRecognizer*)otherGestureRecognizer {
FlutterPlatformViewGestureRecognizersBlockingPolicy
@ FlutterPlatformViewGestureRecognizersBlockingPolicyEager
@ FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded
@ FlutterPlatformViewGestureRecognizersBlockingPolicyDoNotBlockGesture
BOOL flt_hasFirstResponderInViewHierarchySubtree
NSMutableArray * backdropFilterSubviews()
void CubicTo(const flutter::DlPoint &cp1, const flutter::DlPoint &cp2, const flutter::DlPoint &p2) override
void MoveTo(const flutter::DlPoint &p2, bool will_be_closed) override
void LineTo(const flutter::DlPoint &p2) override
void QuadTo(const flutter::DlPoint &cp, const flutter::DlPoint &p2) override
CGMutablePathRef TakePath() const
BOOL touchedEndedWithoutBlocking
Collection of functions to receive path segments from the underlying path representation via the DlPa...
#define FML_DLOG(severity)
#define FML_DCHECK(condition)
instancetype initWithFrame
impeller::RoundRect DlRoundRect
impeller::Matrix DlMatrix
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
std::shared_ptr< ContextGLES > context
A 4x4 matrix using column-major storage.
constexpr auto GetBottom() const
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
constexpr auto GetTop() const
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
constexpr auto GetLeft() const
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
constexpr auto GetRight() const
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.