-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Description
When a Grid has SafeAreaEdges enabled (e.g., SafeAreaRegions.Container) and is initialized with a Scale value other than 1 (e.g., Scale = 0.8 for an entry animation), the SafeAreaEdges configuration stops working correctly on Android.
Unlike on iOS, where the Safe Area constraints are respected regardless of the view's scale transformation, Android seems to ignore the safe area insets once scaling is applied. When the element is animated back to Scale = 1, the content ends up positioned under the camera notch/status bar.
Visual Symptoms on Android:
- Content expands UNDER the camera notch/cutout (overlapping) when scaling up.
- The
SafeAreaEdgesconstraints seem to get "lost" during the scale transformation.
Root Cause Analysis:
It appears Android fails to invalidate the layout or recalculate insets after a Scale transformation.
Steps to Reproduce
- Setup a Grid with
SafeAreaEdges="Container",Opacity="0"andScale="0.8"(simulating an entrance state). - In
OnAppearing, animate both properties:
await Task.WhenAll(
ContentLayer.FadeToAsync(1, 200),
ContentLayer.ScaleToAsync(1, 200)
);- Observe that on Android, the content now overlaps the Status Bar/Notch.
Link to public reproduction project repository
https://github.com/paulober/maui-android-safearea-opacity-bug
Version with bug
10.0.20
Affected platforms
Android
Did you find any workaround?
Yes, but it is extremely "hacky" and requires platform-specific code.
We found that we must manually force a layout invalidation after the animation finishes. However, simply re-setting SafeAreaEdges to the desired value (e.g., Container) does not work because the internal property setter probably checks for equality because then it skips the update if the value hasn't changed.
To make this work on Android, we have to "toggle" the value to something else to force the system to react.
The "Dirty" Workaround:
await ContentLayer.ScaleToAsync(1, 200);
#if ANDROID
// HACK: Force layout invalidation on Android.
// We must change the value to something DIFFERENT than the current state
// to bypass the property equality check and trigger an update.
ContentLayer.SafeAreaEdges = new SafeAreaEdges(_initialRegions == SafeAreaRegions.Container
? SafeAreaRegions.SoftInput : SafeAreaRegions.Container);
ContentLayer.SafeAreaEdges = new SafeAreaEdges(_initialRegions);
#endifWhy this is problematic:
- iOS behavior: On iOS, we want the correct
SafeAreaRegions.Containerset right from the start (initialization). - Android behavior: On Android, keeping that correct value causes the bug. We are forced to write platform-specific logic to toggle the property back and forth just to wake up the layout engine.
We shouldn't need to toggle properties to dummy values to get the Safe Area to respect the notch after a simple Scale animation.