Implementing Edge-to-Edge Design in Android Apps
Problem Statement
Since Android 15 (API 35) became available, developers have encountered UI issues where app content renders behind the status bar by default. Previously, system bars reserved dedicated space for app content, but now implementations must handle insets programmatically to avoid overlap and inconsistent status bar colors. This creates layout challenges, especially with AppBarLayout
and Toolbar elements.
Why Edge-to-Edge Design?
Google now mandates edge-to-edge design implementation for all apps targeting Android 15 and above. This design approach:
- Maximizes screen real estate
- Creates a modern, immersive user experience
- Provides consistent visual behavior across applications
- Reduces compatibility issues with newer device form factors
Solution: Proper Edge-to-Edge Implementation
1. Declare Edge-to-Edge Mode
In your activity's onCreate
, add:
WindowCompat.setDecorFitsSystemWindows(window, false)
This is equivalent to calling EdgeToEdge.enable(this)
from AndroidX Activity library.
2. Handle Status Bar Insets for AppBar
Apply insets exclusively to your AppBarLayout
:
ViewCompat.setOnApplyWindowInsetsListener(appBarLayout) { view, windowInsets ->
val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(top = systemBars.top)
WindowInsetsCompat.CONSUMED
}
3. Set Status Bar Color and Appearance
Configure status bar directly:
WindowCompat.getInsetsController(window, window.decorView).apply {
setAppearanceLightStatusBars(isLightTheme)
// Do not set status bar color - it's handled by your layout background
}
4. Update Your Layout
Key XML modifications:
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/toolbar_background"
android:fitsSystemWindows="false"> <!-- Disable default fitsSystemWindows -->
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
</com.google.android.material.appbar.AppBarLayout>
5. Handle Navigation Bar Insets (Optional)
Avoid bottom UI overlaps:
ViewCompat.setOnApplyWindowInsetsListener(fab) { view, windowInsets ->
val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updateLayoutParams<MarginLayoutParams> {
bottomMargin = systemBars.bottom + defaultMargin
}
WindowInsetsCompat.CONSUMED
}
Handling Compatibility Older SDKs
For backward compatibility:
@RequiresApi(35)
fun enableEdgeToEdge(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
WindowCompat.setDecorFitsSystemWindows(activity.window, false)
} else {
// Legacy edge-to-edge implementation
}
}
Common Pitfalls and Solutions
1. Status Bar Color Mismatch
Problem: AppBar and status bar show different colors
Solution: Remove setStatusBarColor()
calls. Instead:
- Set
AppBarLayout
background color - Ensure background extends behind status bar
2. Content Hiding Behind System Bars
Problem: UI elements obscured by system bars
Solution: Apply insets only to specific views using setOnApplyWindowInsetsListener
, not the root layout.
3. Calculation Errors in Views
Problem: Incorrect height measurements
Solution: Avoid using window.decorView
directly. Instead:
view.doOnLayout {
// Access measured dimensions here
}
4. Layout Flickering on Cold Start
Problem: Transient layout jumps
Solution: Delay inset handling until measured:
if (appBarLayout.isLaidOut) {
applyWindowInsets()
} else {
appBarLayout.doOnLayout { applyWindowInsets() }
}
Complete Implementation
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
WindowCompat.setDecorFitsSystemWindows(window, false)
ViewCompat.setOnApplyWindowInsetsListener(appBar) { view, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(top = systemBars.top)
WindowInsetsCompat.CONSUMED
}
WindowCompat.getInsetsController(window, window.decorView).apply {
setAppearanceLightStatusBars(!isDarkTheme)
}
}
}
Important Notes
- Test across Android versions: Use
Build.VERSION.SDK_INT
checks for version-specific logic - Alternative approach: AndroidX Core provides
WindowCompat.setDecorFitsSystemWindows()
for unified implementation - Design verification: Always test with both light/dark status bar icons
- FAB considerations: Apply bottom insets to floating action buttons
- Legacy support: Use AndroidX Activity 1.9.0+ for consistent behavior on pre-Android 15 devices
Accessibility Recommendation
Always maintain at least 4.5:1 contrast ratio between status bar icons and your background color
Avoid Common Mistake
Never combine android:fitsSystemWindows="true"
with manual insets handling—these approaches conflict
By following this implementation, your app will correctly support edge-to-edge design standards while maintaining backward compatibility with older Android versions.