For several generations of the Android operating system, enabling an edge-to-edge layout—where application content draws seamlessly behind the top status bar and bottom gesture navigation bar—was an optional design recommendation. Developers could choose whether to opt in using APIs like WindowCompat.setDecorFitsSystemWindows(window, false). However, with the release of Android 15 (API level 35), Google made a fundamental architectural shift: edge-to-edge display is now enforced by default for all applications targeting Android 15.
Under Android 15, the operating system renders system bars with transparent or translucent backgrounds, and applications are automatically laid out across the entire physical display. If applications fail to handle insets properly, floating action buttons (FABs), navigation bars, text headers, and input fields will be clipped or obscured directly beneath system controls. This guide explains how edge-to-edge enforcement works, how to inspect inset boundaries, and how to adapt your application layouts using modern WindowInsets APIs.
What Changed in Android 15 Edge-to-Edge Enforcement?
When an application targets targetSdkVersion 35 on an Android 15 device:
- Automatic Edge-to-Edge: The legacy method
WindowCompat.setDecorFitsSystemWindows()is officially deprecated and ignored. The window decor always allows content to draw edge-to-edge. - Transparent Status & Navigation Bars: The status bar and navigation bar backgrounds become 100% transparent by default. Color customization APIs like
window.statusBarColorandwindow.navigationBarColorare deprecated and have no effect. - Dynamic System Icons: The operating system dynamically adjusts status bar icons (clock, battery, Wi-Fi) between dark and light modes based on the luminance of the content rendered directly beneath them.
- Cutout & Display Notches: Content automatically extends into the display cutout and camera hole-punch areas unless explicitly padded.
Understanding WindowInsets Types
To prevent UI elements from clashing with system overlays, Android provides the WindowInsets architecture. Insets define the exact pixel distances by which content must be indented to remain fully interactive:
| WindowInsets Type | Protected Area | Typical UI Application |
|---|---|---|
| statusBars() | Top system status bar (clock, notifications, battery) | App bars, toolbars, top search headers |
| navigationBars() | Bottom gesture navigation handle or 3-button bar | Bottom navigation bars, bottom sheets, FABs |
| systemBars() | Combines statusBars(), navigationBars(), and caption bars |
Standard general container padding |
| displayCutout() | Physical camera punch-holes and curved screen corners | Landscape full-screen media and edge controls |
| ime() | On-screen software keyboard (Input Method Editor) | Chat input fields, login forms, search inputs |
Implementing Inset Handling in Jetpack Compose
In modern Android applications built with Jetpack Compose, handling insets is integrated natively into layouts and modifiers:
1. Using Scaffold for Automatic Inset Consumption
The standard Material 3 Scaffold component automatically manages system bar padding for top app bars and bottom navigation bars:
@Composable
fun MainScreen() {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Android 15 Edge-to-Edge") }
)
},
bottomBar = {
NavigationBar {
// Navigation items automatically receive navigationBar padding
}
}
) { innerPadding ->
// MUST apply innerPadding to the root content container
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
Text("Main interactive content stays safe from clipping.")
}
}
}
2. Applying Custom Inset Modifiers
If you build custom containers without Scaffold, use Compose inset modifiers directly:
// Pad container away from status and navigation bars
Column(
modifier = Modifier
.fillMaxSize()
.windowInsetsPadding(WindowInsets.safeDrawing)
) {
// Child UI components
}
// Automatically animate alongside the on-screen keyboard
Box(
modifier = Modifier
.fillMaxSize()
.imePadding()
) {
ChatInputField(modifier = Modifier.align(Alignment.BottomCenter))
}
Handling Insets in Traditional XML Layouts
For applications using Android Views and XML layouts, attach an OnApplyWindowInsetsListener in your Activity or Fragment:
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root_container)) { view, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
// Apply padding to root container to preserve touch targets
view.setPadding(
systemBars.left,
systemBars.top,
systemBars.right,
systemBars.bottom
)
// Return CONSUMED or forward insets to child views
WindowInsetsCompat.CONSUMED
}
Testing and Debugging Edge-to-Edge Layouts
- Test with 3-Button and Gesture Navigation: Switch between 3-button navigation (which creates a taller 48dp bottom bar) and gesture navigation (which creates a thin 16dp bar) in Settings > System > Navigation mode to verify that bottom buttons are never obscured.
- Audit Landscape Orientation: Rotate your device 90 degrees to confirm that camera cutouts and lateral navigation bars do not clip critical interactive components.
- Verify Keyboard Insets: Tap inside text fields to ensure the keyboard animates smoothly without covering submit buttons or active text lines.
By adopting the WindowInsets framework proactively for Android 15, Android engineers ensure their applications deliver stunning, immersive visual experiences while maintaining flawless touch target accessibility.