Changing Status Bar Color in Android 15+
Problem Statement
When migrating apps to Android 15 (API 35+), developers encounter a breaking change: the traditional window.statusBarColor
property is deprecated and no longer functional. The new requirement is to draw backgrounds using the WindowInsets
API. This shift aims to enforce edge-to-edge designs but poses challenges when applying custom status bar colors (e.g., static colors like RED or GREEN) in a backward-compatible way.
Recommended Solution: Using WindowInsets
For Android 15+, handle status bar coloring by:
- Listening to WindowInsets changes
- Setting the decor view's background color
- Applying top padding to avoid content overlap
Kotlin Implementation
import android.graphics.Color
import android.os.Build
import android.view.View
import android.view.Window
import android.view.WindowInsets
fun setStatusBarColor(window: Window, targetColor: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { // Android 15+
window.decorView.setOnApplyWindowInsetsListener { view, insets ->
// Extract the status bar height
val statusBarHeight = insets.getInsets(WindowInsets.Type.statusBars()).top
// Apply background color and padding
view.setBackgroundColor(targetColor)
view.setPadding(0, statusBarHeight, 0, 0)
// Return the original insets
insets
}
} else { // Android 12-14 (API 31-34)
@Suppress("DEPRECATION")
window.statusBarColor = targetColor
}
}
Usage: Call setStatusBarColor(window, Color.RED)
in your activity's onCreate
.
Java Equivalent
View decorView = getWindow().getDecorView();
decorView.setOnApplyWindowInsetsListener((v, insets) -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
Insets statusBarInsets = insets.getInsets(WindowInsets.Type.statusBars());
v.setBackgroundColor(ContextCompat.getColor(this, R.color.red));
v.setPadding(0, statusBarInsets.top, 0, 0);
}
return insets;
});
Key Details
- Edge-to-Edge Requirement: Ensures compatibility with gesture navigation and system UI overlays
- Backward Compatibility: The
Build.VERSION_CODES
check maintains functionality on older OS versions - Custom Colors: Directly pass any color resource or hex value, bypassing theme-based colors
Temporary Workaround (Avoid Long-Term)
Opt out of edge-to-edge enforcement using windowOptOutEdgeToEdgeEnforcement
. This restores statusBarColor
behavior but is marked for deprecation. Use only for short-term mitigation:
In themes.xml
(API 35+)
<!-- res/values-v35/themes.xml -->
<resources>
<style name="Theme.App" parent="Theme.Material3.DayNight">
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
<item name="android:statusBarColor">@color/your_custom_color</item>
</style>
</resources>
Manifest Application/Activity Tag
<application
android:theme="@style/Theme.App"
android:windowOptOutEdgeToEdgeEnforcement="true">
DANGER
Warning: This is a temporary fix. Google will remove this property in future Android releases, potentially breaking your app. Prefer the WindowInsets
solution for production apps.
Best Practices Explained
Why
WindowInsets
?
Android 15 enforces proper background management behind system bars. Drawing directly via insets conforms to edge-to-edge design standards and prevents visual artifacts with transient system bars (e.g., notifications).Padding Requirement
Setting top padding matching the status bar height prevents critical UI elements (like buttons or titles) from being obscured by the status bar. Without this, content might render under the status bar area.Avoid DecorView Hacks
Solutions like.setBackgroundColor()
without padding adjustments (e.g.,window.decorView.setBackgroundColor(Color.RED)
) cause overlapping content issues. Always account for insets.
Jetpack Compose Alternative
For declarative UIs:
setContent {
CompositionLocalProvider(
LocalStatusBarStyle provides YourAppColors.statusBarStyle()
) {
Box(
Modifier
.fillMaxSize()
.windowInsetsTopHeight(WindowInsets.statusBars)
.background(Color.Red)
) {
// Your app content
}
}
}
Key modifier: .windowInsetsTopHeight(WindowInsets.statusBars)
ensures the status bar area is reserved and colored.
Final Recommendation
Use the WindowInsets
approach for Android 15+ compatibility. It aligns with modern UI standards, avoids deprecation risks, and provides reliable behavior across devices. Reserve the windowOptOutEdgeToEdgeEnforcement
workaround for temporary migrations only.