@@ -324,15 +324,13 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
324324 private let container : NavigationClipView
325325 private let material = UIVisualEffectView ( effect : UIBlurEffect ( style : . systemUltraThinMaterial ) )
326326 private let materialFade = CAGradientLayer ( )
327- // A transparent, noninteractive viewport provides the system status fade
328- // independently of the layout-only scroll view that collapses large titles.
329- private let edgeScrollView = UIScrollView ( )
330327 private let content = NavigationContentController ( )
331328 private let navigation : UINavigationController
332329 private let channel : FlutterMethodChannel
333330 private var offset : CGFloat = 0
334331 private var controlBackground = UIColor . secondarySystemBackground
335332 private var alwaysFrosted = false
333+ private var backdropOnly = false
336334 private var usesSystemScrollEdge = false
337335 private var expandedBarHeight : CGFloat = 0
338336 private var titleIsCollapsed : Bool ?
@@ -367,18 +365,6 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
367365 materialFade . locations = [ 0 , 0.55 , 1 ]
368366 material . layer . mask = materialFade
369367 container . addSubview ( material )
370- if #available( iOS 27 . 0 , * ) {
371- edgeScrollView . accessibilityIdentifier = " navigation-status-edge "
372- edgeScrollView . isUserInteractionEnabled = false
373- edgeScrollView . scrollsToTop = false
374- edgeScrollView . accessibilityElementsHidden = true
375- edgeScrollView . backgroundColor = . clear
376- edgeScrollView . contentInsetAdjustmentBehavior = . never
377- edgeScrollView . contentSize = CGSize ( width : 1 , height : 10000 )
378- edgeScrollView . topEdgeEffect . style = . soft
379- edgeScrollView . bottomEdgeEffect . isHidden = true
380- container . addSubview ( edgeScrollView )
381- }
382368 navigation . view . backgroundColor = . clear
383369 navigation . navigationBar . isTranslucent = true
384370 parent ? . addChild ( navigation )
@@ -411,14 +397,19 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
411397 // needed for UIKit's scroll-edge and large-title calculations.
412398 guard ! measuring else { return }
413399 let viewportHeight = container . window ? . bounds . height ?? container . bounds . height
414- edgeScrollView . frame = CGRect ( x : 0 , y : 0 , width : container . bounds . width , height : viewportHeight )
415- navigation . view . frame = edgeScrollView . frame
400+ navigation . view . frame = CGRect ( x : 0 , y : 0 , width : container . bounds . width , height : viewportHeight )
416401 // Flutter's route can begin offscreen, where UIKit infers a zero safe area.
417402 // Keep the toolbar below the status region throughout that transition.
418403 // Read the unmodified host inset, never the inset we are adjusting.
419404 // During a slide UIKit may not yet have applied the previous adjustment.
420405 let automaticTop = container . safeAreaInsets . top
421- let requiredTop = max ( 0 , flutterSafeTop - automaticTop )
406+ // An empty native bar supplies the same edge geometry beneath a custom
407+ // Flutter header. Place its lower edge at the end of that header so the
408+ // system fade protects the filters as well as the status area.
409+ let protectedTop = backdropOnly
410+ ? max ( flutterSafeTop , container . bounds . height - max ( 44 , navigation . navigationBar . bounds . height ) )
411+ : flutterSafeTop
412+ let requiredTop = max ( 0 , protectedTop - automaticTop )
422413 if abs ( navigation . additionalSafeAreaInsets . top - requiredTop ) > 0.5 {
423414 navigation . additionalSafeAreaInsets . top = requiredTop
424415 }
@@ -473,35 +464,35 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
473464 flutterSafeTop = ( args [ " safeTop " ] as? NSNumber ) ? . doubleValue ?? 0
474465 navigation . overrideUserInterfaceStyle = args [ " dark " ] as? Bool == true ? . dark : . light
475466 material . overrideUserInterfaceStyle = navigation . overrideUserInterfaceStyle
476- edgeScrollView . overrideUserInterfaceStyle = navigation . overrideUserInterfaceStyle
477467 // Let the ultra-thin system material provide its own adaptive tint.
478468 material . contentView . backgroundColor = . clear
479469 let bar = navigation . navigationBar
480470 let color = Self . color ( args [ " foreground " ] )
481471 bar . tintColor = color
482472 titleColor = color
483473 largeTitleOpacity = - 1
474+ backdropOnly = args [ " backdropOnly " ] as? Bool == true
475+ container . isUserInteractionEnabled = ! backdropOnly
476+ navigation . view . isUserInteractionEnabled = ! backdropOnly
477+ navigation . view . accessibilityElementsHidden = backdropOnly
484478 let largeTitle = args [ " largeTitle " ] as? Bool == true
485479 alwaysFrosted = args [ " alwaysFrosted " ] as? Bool == true
486480 if #available( iOS 26 . 0 , * ) {
487481 if #available( iOS 27 . 0 , * ) { usesSystemScrollEdge = true }
488482 content . scrollView . topEdgeEffect . style = . soft
489- content . scrollView . topEdgeEffect . isHidden = ! usesSystemScrollEdge || ! alwaysFrosted || largeTitle
490- edgeScrollView . isHidden = alwaysFrosted && ! largeTitle
483+ content . scrollView . topEdgeEffect . isHidden = ! usesSystemScrollEdge
491484 }
492- // Large-title pages need material across the whole header: the stationary
493- // system edge only blurs the status area, leaving expanded text over rows.
494- // iOS 26 does not composite this native edge over sibling Flutter content
495- // in our embedding, so retain the ultra-thin material fallback there.
496- // Conversations use the navigation controller's native edge. Other pages
497- // use a stationary native edge so UIKit's large-title inset adjustments
498- // cannot hide the status-area fade when the title expands or collapses.
485+ // Older embedded views retain material because their native edge cannot
486+ // sample Flutter content. On iOS 27, UIKit also suppresses its native
487+ // edge while the large-title inset is collapsing.
488+ // Keep the graduated material throughout that transition so partial
489+ // scrolling never leaves the title and status area unprotected.
499490 material . isHidden = usesSystemScrollEdge && ! largeTitle
500491 // Let the native fade extend past the bar without enlarging its hit area.
501492 // The mirrored content is transparent and the bottom edge is hidden.
502493 container . clipsToBounds = ! usesSystemScrollEdge
503494 let appearance = UINavigationBarAppearance ( )
504- if usesSystemScrollEdge && ! largeTitle {
495+ if usesSystemScrollEdge {
505496 appearance . configureWithDefaultBackground ( )
506497 } else {
507498 appearance . configureWithTransparentBackground ( )
0 commit comments