Question
After updating an Android project to target API level 30, the View.systemUiVisibility property is deprecated. The Kotlin code below is equivalent to Java's setSystemUiVisibility():
copyPlayerView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_LOW_PROFILE or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
)
What is the stable replacement for this code? Android documentation recommends WindowInsetsController, but how should it be used to hide the system bars and enable immersive behavior?
Short Answer
You will learn why systemUiVisibility was deprecated, how its flags map to the newer Window Insets APIs, and how to use WindowInsetsControllerCompat in Kotlin for an immersive Android screen that works across Android versions.
Concept
View.systemUiVisibility used one integer containing many bit flags to control the status bar and navigation bar. It could hide bars, request fullscreen layout, and configure immersive mode.
Android now separates those responsibilities through the Window Insets APIs:
WindowCompat.setDecorFitsSystemWindows()controls whether app content is laid out around system bars or can draw edge to edge.WindowInsetsControllerCompat.hide()hides selected system bars.WindowInsetsControllerCompat.systemBarsBehaviorcontrols how hidden bars reappear after user interaction.
This separation is useful because system bars are part of the window, not just one view. The newer API also works better with display cutouts, gesture navigation, keyboard insets, and modern edge-to-edge layouts.
For most applications, prefer the AndroidX compatibility version, WindowInsetsControllerCompat. It provides one API for modern Android versions and older supported versions. The platform WindowInsetsController is available directly from API level 30.
Mental Model
Think of the app window as a room and the system bars as movable curtains.
The old systemUiVisibility API was a single remote with many buttons pressed at once. You combined flags into one number and assigned it to a view.
The new API gives each job its own control:
setDecorFitsSystemWindows(false)decides whether your furniture can extend behind the curtains.hide(...)closes the status and navigation-bar curtains.systemBarsBehaviordecides what happens when a user swipes near a hidden curtain.
This makes it clearer which setting controls layout, visibility, and user interaction.
Syntax and Examples
For a media player or fullscreen screen, use AndroidX Core's window-insets APIs.
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
fun enableImmersiveMode() {
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).apply {
hide(
WindowInsetsCompat.Type.statusBars() or
WindowInsetsCompat.Type.navigationBars()
)
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
Call this from an Activity, where window is available. If the code is inside a Fragment, use the hosting activity's window:
val activityWindow = requireActivity().window
WindowCompat.setDecorFitsSystemWindows(activityWindow, false)
WindowInsetsControllerCompat(activityWindow, activityWindow.decorView).apply {
hide(WindowInsetsCompat.Type.systemBars())
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
Type.systemBars() is a shortcut for the status bar and navigation bar. The behavior constant is the modern equivalent of the important user-facing behavior of SYSTEM_UI_FLAG_IMMERSIVE_STICKY: a swipe temporarily reveals the bars, then they hide again.
Step by Step Execution
Consider this fullscreen player setup:
WindowCompat.setDecorFitsSystemWindows(window, false)
val controller = WindowInsetsControllerCompat(window, window.decorView)
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(WindowInsetsCompat.Type.systemBars())
Step by step:
setDecorFitsSystemWindows(window, false)tells Android that the app may draw behind system bars. This replaces layout-related flags such asSYSTEM_UI_FLAG_LAYOUT_STABLEandSYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.WindowInsetsControllerCompat(...)creates a controller for the activity window.systemBarsBehavioris set before hiding the bars. A user can swipe from an edge to reveal the bars temporarily.hide(Type.systemBars())requests that Android hide both the status bar and navigation bar.- The user can still access system controls with the configured gesture. Apps must not permanently prevent users from accessing navigation controls.
Real World Use Cases
Common uses of Window Insets controllers include:
- Video players: hide controls and system bars while a video plays fullscreen.
- Photo viewers: provide an unobstructed image preview, then reveal controls on touch.
- Games: draw across the whole display while allowing transient system-bar access.
- Reading screens: offer a distraction-free reading mode.
- Camera previews: allow preview content to extend behind bars while carefully placing interactive controls outside unsafe areas.
- Immersive onboarding: temporarily present an animation or experience without persistent system chrome.
For ordinary app screens, hiding system bars is usually unnecessary. Edge-to-edge layout with visible, readable bars is often the better choice.
Real Codebase Usage
In production code, keep window configuration in a small, reusable function rather than repeating it in every screen.
import android.app.Activity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
fun Activity.enterFullscreenPlayer() {
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
hide(WindowInsetsCompat.Type.systemBars())
}
}
fun Activity.exitFullscreenPlayer() {
WindowInsetsControllerCompat(window, window.decorView).show(
WindowInsetsCompat.Type.systemBars()
)
}
A player screen may call enterFullscreenPlayer() when playback enters fullscreen and exitFullscreenPlayer() when the user exits the player.
Also handle insets for controls that must not be covered by gesture areas or bars. For example, a bottom playback-control bar can add bottom padding based on the navigation-bar inset:
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
ViewCompat.setOnApplyWindowInsetsListener(playbackControls) { view, insets ->
val bars = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
view.setPadding(
view.paddingLeft,
view.paddingTop,
view.paddingRight,
bars.bottom
)
insets
}
This is especially important when content draws edge to edge.
Common Mistakes
Using the platform controller without considering older Android versions
This only works on API level 30 and later:
// Requires API 30+
window.insetsController?.hide(WindowInsets.Type.systemBars())
Avoid version checks in most apps by using WindowInsetsControllerCompat instead.
Hiding bars but not enabling edge-to-edge layout
controller.hide(WindowInsetsCompat.Type.systemBars())
This hides bars, but it does not explicitly configure whether content can draw behind them. For immersive content, usually also use:
WindowCompat.setDecorFitsSystemWindows(window, false)
Treating old flags as a one-to-one replacement
Not every old flag has an exact equivalent:
FULLSCREENbecomes hidingstatusBars().HIDE_NAVIGATIONbecomes hidingnavigationBars().- Layout flags become
setDecorFitsSystemWindows(false). IMMERSIVE_STICKYbecomes a system-bars behavior.LOW_PROFILEis not normally needed when the bars are hidden.
Forgetting to restore bars
If fullscreen is temporary, call show() when leaving it:
controller.show(WindowInsetsCompat.Type.systemBars())
Assuming the app can lock users out of system navigation
Android intentionally keeps system navigation accessible. Immersive behavior requests hidden bars, but the user can reveal them with system gestures.
Using a child view as if it owns the window
systemUiVisibility was assigned to a view, but the modern controller controls a window. Create it with the activity window and its decor view:
WindowInsetsControllerCompat(window, window.decorView)
Comparisons
Old systemUiVisibility flag | Modern approach | Purpose |
|---|---|---|
SYSTEM_UI_FLAG_FULLSCREEN | hide(Type.statusBars()) | Hide the status bar. |
SYSTEM_UI_FLAG_HIDE_NAVIGATION | hide(Type.navigationBars()) | Hide the navigation bar. |
| Both flags together | hide(Type.systemBars()) | Hide both system bars. |
SYSTEM_UI_FLAG_LAYOUT_STABLE | WindowCompat.setDecorFitsSystemWindows(window, false) | Use an edge-to-edge layout strategy. |
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | WindowCompat.setDecorFitsSystemWindows(window, false) | Let content extend behind navigation areas. |
SYSTEM_UI_FLAG_IMMERSIVE_STICKY | BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE | Reveal bars temporarily after an edge swipe. |
SYSTEM_UI_FLAG_LOW_PROFILE | Usually omit it; on API 30+ use platform appearance APIs if truly required | Dim system-bar icons instead of hiding bars. |
| API | Best choice |
|---|---|
| App supports multiple Android versions | WindowInsetsControllerCompat and WindowCompat from AndroidX Core. |
| App runs only on API 30+ | Platform WindowInsetsController is available, but AndroidX is still often simpler. |
| Screen only needs edge-to-edge content | Use setDecorFitsSystemWindows(false) and apply insets to important controls; do not necessarily hide bars. |
Cheat Sheet
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
val controller = WindowInsetsControllerCompat(window, window.decorView)
// Allow content to draw behind system bars.
WindowCompat.setDecorFitsSystemWindows(window, false)
// Hide both status and navigation bars.
controller.hide(WindowInsetsCompat.Type.systemBars())
// Hide only one bar.
controller.hide(WindowInsetsCompat.Type.statusBars())
controller.hide(WindowInsetsCompat.Type.navigationBars())
// Let an edge swipe reveal hidden bars temporarily.
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Restore bars.
controller.show(WindowInsetsCompat.Type.systemBars())
Quick rules:
- Prefer
WindowInsetsControllerCompatfor compatibility. - Configure the window, not only a child view.
hide()controls visibility;setDecorFitsSystemWindows(false)controls layout.- Apply insets as padding or margins to buttons and controls that must remain reachable.
- Restore system bars when fullscreen mode ends.
FAQ
Why is systemUiVisibility deprecated?
It combines several unrelated window and system-bar behaviors in one set of view flags. Window Insets APIs provide clearer controls for layout, visibility, and behavior.
What replaces SYSTEM_UI_FLAG_FULLSCREEN in Kotlin?
Hide the status bar:
controller.hide(WindowInsetsCompat.Type.statusBars())
What replaces SYSTEM_UI_FLAG_HIDE_NAVIGATION?
Hide the navigation bar:
controller.hide(WindowInsetsCompat.Type.navigationBars())
What replaces SYSTEM_UI_FLAG_IMMERSIVE_STICKY?
Use:
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
Does WindowInsetsControllerCompat work below Android 11?
Yes. It is the AndroidX compatibility API designed to provide a consistent interface across supported Android versions.
Is SYSTEM_UI_FLAG_LOW_PROFILE required for immersive mode?
No. Low-profile mode dims system-bar icons, while immersive mode normally hides the bars. If the bars are hidden, low-profile appearance has no visible benefit.
How do I show the system bars again?
Call:
controller.show(WindowInsetsCompat.Type.systemBars())
Should every screen hide system bars?
No. Reserve fullscreen or immersive behavior for cases such as video playback, games, camera previews, and photo viewers. Standard app screens should generally preserve easy access to system controls.
Mini Project
Description
Build a small fullscreen media-player mode for an Android activity. It demonstrates entering immersive mode for playback and restoring normal system bars when the user leaves fullscreen.
Goal
Create buttons that hide and show the status and navigation bars using WindowInsetsControllerCompat.
Requirements
Use WindowCompat.setDecorFitsSystemWindows(window, false) when fullscreen mode starts.
Hide both the status bar and navigation bar.
Use transient swipe behavior so users can reveal system bars temporarily.
Provide a way to exit fullscreen and show the bars again.
Use AndroidX compatibility APIs rather than deprecated systemUiVisibility.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.