Don't Let Android 15 Break Your MAUI App: The 3-Step Edge-to-Edge Fix
"Stop wrestling with compatibility mode. Learn why your app looks like a tiny box on Android 15 and how a single XAML style fixes it forever."
Don't Let Android 15 Break Your MAUI App: The 3-Step Edge-to-Edge Fix
You updated your .NET MAUI app to net10.0-android, shipped a new release, and suddenly the app opens as a tiny box in the center of the screen with a black background all around it. It looks like an Android bug β but the root cause is well understood, and the fix is permanent.
In this post you'll learn exactly what happened, why it happened, and how to fix it in a way that's compatible with Android 15 and Android 16.
What Changed in Android 15 and .NET 10
When Google released Android 15 (API 35), it started enforcing edge-to-edge mode for all apps. This means app content is expected to extend behind the system bars (status bar and navigation bar).
At the same time, .NET 10 introduced a silent breaking change in the default behavior of MAUI pages:
In .NET 9,
ContentPagerespected system bars by default (Containerbehavior). In .NET 10,ContentPagedefaults to edge-to-edge (None).
The combination of both changes causes apps without the correct configuration to fall into Android's compatibility mode β which renders the layout at a fixed small size and centers it on screen with a black background. That's the "small box" you're seeing.
β οΈ Breaking Change in .NET 10
If you migrated from net9.0-android to net10.0-android, your pages' default behavior changed without you touching a single line of XAML.
Symptoms
You're likely hitting this issue if:
- The app opens as a small centered window with a black background around it
- After the splash screen, there's a black screen before the app loads
- Content is partially hidden behind the status bar or navigation bar
- The problem appeared after a version bump or package update to .NET 10
Diagnosis: The Three Root Causes
1. android:resizeableActivity Not Declared
Android 15 treats apps without this declaration as "not optimized" and forces them into compatibility mode. The result is the small box.
2. EdgeToEdge.Enable(this) Without Inset Handling
This API correctly activates edge-to-edge, but it requires the app to handle Window Insets β otherwise content ends up behind the system bars and the window background becomes transparent (black).
3. SafeAreaEdges Not Configured for .NET 10
Due to the .NET 10 breaking change, all ContentPage instances now default to None. Without explicitly setting SafeAreaEdges, layouts no longer respect safe areas.
The Complete Fix
Step 1 β AndroidManifest.xml: Declare Screen Support and Resizable Activity
Add android:resizeableActivity="true" to the <application> tag and the <supports-screens> element right below it:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/appicon"
android:label="${applicationLabel}"
android:usesCleartextTraffic="true"
android:windowOptOutEdgeToEdgeEnforcement="true"
android:resizeableActivity="true">
</application>
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true" />
<!-- your permissions here -->
</manifest>
βΉοΈ About windowOptOutEdgeToEdgeEnforcement
This attribute disables Android 15's forced edge-to-edge enforcement. It's a temporary escape hatch β in Android 16 it will be ignored entirely. That's why Step 3 is critical for long-term compatibility.
Step 2 β MainActivity.cs: Remove EdgeToEdge.Enable
If you added EdgeToEdge.Enable(this) to OnCreate, remove it. Without proper inset handling, it makes the window transparent and causes the black screen between splash and first render:
// BEFORE β causes black screen without inset handling
protected override void OnCreate(Bundle? savedInstanceState)
{
EdgeToEdge.Enable(this); // β remove this
base.OnCreate(savedInstanceState);
}
// AFTER β clean and correct
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
Step 3 β App.xaml: Configure SafeAreaEdges Globally
This is the definitive fix β compatible with Android 16. Add a global implicit style to App.xaml to restore .NET 9 behavior across all pages at once:
<Application.Resources>
<ResourceDictionary>
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="SafeAreaEdges" Value="Container" />
</Style>
<!-- rest of your resources -->
</ResourceDictionary>
</Application.Resources>
β One Style, All Pages
Because ApplyToDerivedTypes="True" is set, this style applies to every page in your app β including Shell pages and custom page subclasses β without touching individual XAML files.
Why SafeAreaEdges="Container" Is the Right Fix
SafeAreaEdges was introduced in .NET MAUI 10 as the official API for safe area control. It operates at the MAUI layer using Window Insets β the modern Android API β and automatically adapts to any OS version.
| Value | Behavior |
|---|---|
None |
Full edge-to-edge β content can extend behind system bars |
Container |
Respects system bars and notches (same as .NET 9 default) |
SoftInput |
Respects only the soft keyboard inset |
All |
Respects everything, including the keyboard |
The key advantage over manifest-level workarounds: it works at runtime via Window Insets, so it adapts correctly regardless of Android version β including Android 16, which will remove the windowOptOutEdgeToEdgeEnforcement opt-out entirely.
Solution Comparison
| Approach | Android 15 | Android 16 | Complexity |
|---|---|---|---|
windowOptOutEdgeToEdgeEnforcement only |
β Works | β Removed | Low |
EdgeToEdge.Enable without insets |
β Works | β Works | High β needs per-page inset handling |
SafeAreaEdges="Container" global style |
β Works | β Works | Low β one line |
Special Cases: Overriding Per Page
The global Container style covers the vast majority of screens. For specific pages that need different behavior, simply override the property inline:
Immersive Page (photo viewer, custom splash)
<ContentPage SafeAreaEdges="None">
<!-- content extends behind system bars -->
</ContentPage>
Page With Input at the Bottom (chat, forms)
<ContentPage SafeAreaEdges="All">
<!-- keyboard won't cover content -->
</ContentPage>
Hybrid Layout (edge-to-edge header + safe content area)
<ContentPage SafeAreaEdges="None">
<Grid RowDefinitions="Auto,*">
<!-- Header bleeds behind the status bar -->
<Grid BackgroundColor="{StaticResource PrimaryColor}"
SafeAreaEdges="None" />
<!-- Content respects safe areas -->
<ScrollView Grid.Row="1"
SafeAreaEdges="Container" />
</Grid>
</ContentPage>
βΉοΈ ScrollView and SafeAreaEdges
Setting SafeAreaEdges directly on a ScrollView has no effect for keyboard avoidance. Wrap the ScrollView inside a layout and set SafeAreaEdges on the container instead.
Migration Checklist: .NET 9 β .NET 10 (Android)
- Add
android:resizeableActivity="true"to the<application>tag in the manifest - Add
<supports-screens>with all screen sizes enabled - Remove
EdgeToEdge.Enable(this)fromMainActivity.cs - Add global
SafeAreaEdges="Container"implicit style inApp.xaml - Test on a physical device running Android 14 or higher
- Test on a device with a notch or punch-hole camera
- Verify that keyboard does not overlap input fields on entry-heavy pages
Frequently Asked Questions
Will this break older Android versions?
No. SafeAreaEdges uses Window Insets, which is available and safe across all Android versions supported by .NET MAUI (minSdk 23+). On older devices the behavior is identical to before.
Do I need to update every XAML file?
No. The implicit global style in App.xaml applies to every ContentPage automatically. You only need to add per-page overrides for screens that intentionally need different behavior.
Can I keep windowOptOutEdgeToEdgeEnforcement="true" in the manifest?
Yes, for now. It doesn't hurt and provides an extra safety net on Android 15. Just be aware it will be removed in Android 16, which is why SafeAreaEdges="Container" is the long-term solution.
What about iOS?
SafeAreaEdges works on iOS too, replacing the legacy ios:Page.UseSafeArea="True" attribute. If you were using that, you can safely migrate to SafeAreaEdges="Container" as well.
Conclusion
The migration to net10.0-android introduced a silent breaking change that affects every MAUI app: the default behavior of ContentPage shifted from Container to None. Combined with Android 15's new edge-to-edge enforcement rules, the result is the small box layout or a black screen on startup.
The fix takes three lines of XAML in App.xaml β but it's important to apply it now, before Android 16 reaches your users' devices and the manifest opt-out stops working altogether.
If you maintain multiple MAUI apps, apply all three steps to each one. The effort is minimal, and you'll be covered for the next wave of Android releases.
Related reads
Explore by topic
FAQ
What Changed in Android 15 and .NET 10
When Google released Android 15 (API 35), it started enforcing edge-to-edge mode for all apps. This means app content is expected to extend behind the system bars (status bar and navigation bar).
