Cooking Window Inset with Jetpack Compose sauce and a pinch of View — part 1
Published: 2023-02-28 17:24:59

Hey! My name is Timur, I am an Android developer at [KTS](https://www.kts.solutions/).

Unfortunately, there’re still Android applications that don’t support _edge-to-edge_. I feel that developers either don’t know about this possibility or are afraid to work with _WindowInsets_. In fact, Edge-to-Edge isn’t difficult to implement, and thanks to this article you’ll understand this topic much faster.

Today I’ll explain what _edge-to-edge_ mode is in mobile apps and how to work with _WindowInsets_ in Android. In the next article, we’ll also cover examples of how to handle insets not only in View, but also in _Jetpack Compose_.

When you can find the articles about working with _Insets_ in _View_ on the web, the information about working with them in _Jetpack Compose_ can only be found in the official documentation.

All the examples from the article can be viewed in [this](https://github.com/TimurChikishev/Insets/) repository.

The content of the article:

1️⃣ What is edge-to-edge?
2️⃣ Steps for setting up edge-to-edge
3️⃣ Changing the color of the system UI
4️⃣ Request a rendering of the application under the system UI
5️⃣ Eliminate visual conflicts
6️⃣ WindowInsets vs fitSystemWindow?

**What is edge-to-edge?**
Today, mobile applications are increasingly displayed across the entire visible surface of the display, beyond the system UI. These applications use an _edge-to-edge_ approach, where the application is displayed below the system UI, i.e., the status bar and the navigation bar.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v377g9gpgoqupa074cla.jpeg)

Why, you ask? To create a more attractive and modern user interface. Will you agree that everyone is happier when they use a nicer application?

Now let’s move on to the _edge-to-edge_ implementation.

**Steps for setting up edge-to-edge**

To implement _edge-to-edge_ mode in your application you’ll need to:

- change the color of the system UI
- request a rendering of the application under the system UI
- eliminate visual conflicts

**Changing the color of the system UI**

As of Android 5 (API 21), it’s now possible to set the color for the Status Bar and Navigation Bar. Use the following theme attributes for this purpose:

```
<item name="android:statusBarColor">@color/colorAccent</item>
<item name="android:navigationBarColor">@color/colorAccent</item>
```
We can also make the color of the system UI transparent or semi-transparent. To achieve semi-transparency, just set [android:windowTranslucentStatus](https://developer.android.com/reference/android/R.attr#windowTranslucentStatus) and [android:windowTranslucentNavigation](https://developer.android.com/reference/android/R.attr#windowTranslucentNavigation):

```
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3xjkdbjgklvvbf8vzjy.jpeg)

To get a fully transparent system interface you need to set [android:navigationBarColor](https://developer.android.com/reference/android/R.attr#navigationBarColor) and [android:statusBarColor](https://developer.android.com/reference/android/R.attr#statusBarColor) with transparent color and disable contrast with the following attributes [android:enforceNavigationBarContrast](https://developer.android.com/reference/android/view/Window#isNavigationBarContrastEnforced()), [android:enforceStatusBarContrast](https://developer.android.com/reference/android/view/Window#isStatusBarContrastEnforced()). Disabling contrast is necessary because since version 10 Android provides sufficient contrast to the Navigation Bar.

```
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="android:enforceStatusBarContrast">false</item>
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/twqthdmwlkv3fh1hqyzk.jpg)

In the screenshot above, you may see that the Navigation Bar buttons are barely visible. The Status Bar would have the same problem if it weren’t for the magenta color. To fix this, use the attributes [android:windowLightStatusBar](https://developer.android.com/reference/android/R.attr#windowLightStatusBar), [android:windowLightNavigationBar](https://developer.android.com/reference/android/R.attr#windowLightNavigationBar). Note that [windowLightStatusBar](https://developer.android.com/reference/android/R.attr#windowLightStatusBar) is available with 23 api and [windowLightNavigationBar](https://developer.android.com/reference/android/R.attr#windowLightNavigationBar) with 27 api.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sly41xphiw0hmk7arqn3.jpg)

In _Jetpack Compose_, you can use the [System UI Controller](https://github.com/google/accompanist/tree/main/systemuicontroller) library to change the color, which provides simple tools for changing the color of the UI system. You can change the color of the UI system using this library as follows:

```
private val BlackScrim = Color(0f, 0f, 0f, 0.3f) // 30% opaque black

@Composable
fun TransparentSystemBars() {
   val systemUiController = rememberSystemUiController()
   val useDarkIcons = MaterialTheme.colors.isLight
   SideEffect {
       systemUiController.setSystemBarsColor(
           color = Color.Transparent,
           darkIcons = useDarkIcons,
           isNavigationBarContrastEnforced = false,
           transformColorForLightContent = { original ->
               BlackScrim.compositeOver(original)
           }
       )
   }
}
```
Example of using the TransparentSystemBars function:

```
override fun onCreate(savedInstanceState: Bundle?) { 
   super.onCreate(savedInstanceState)
   WindowCompat.setDecorFitsSystemWindows(window, false)
   setContent {
       TransparentSystemBars()
       Sample()
   }
}
```
The _setSystemBarsColor_ method allows you to:

- set the color for the system UI
- specify when to use light or dark icons
- you can disable contrast with _isNavigationBarContrastEnforced_
- you can use the _transformColorForLightContent_ lambda, which will be called to convert colors if dark icons are requested but not available. The default behavior in _transformColorForLightContent_ is black, so you don’t need to write that).

**Request a rendering of the application under the system UI**

In addition to changing the color of the system interface, you need to tell the system to draw the application in full-screen mode. Android has special _View_ SYSTEM_UI_FLAGS for this purpose (hereafter UI _FLAGS). They’ve been deprecated since API 30, and you should now use the new WindowCompat class, which contains the required flags on earlier versions of the API.

The full screen mode request via [WindowCompat](https://developer.android.com/reference/androidx/core/view/WindowCompat) looks like this:

```
WindowCompat.setDecorFitsSystemWindows(window, false)
```
If you apply this in the activity, the framework won’t replace inserts for the content of your application and we’ll have to do it manually.

It makes sense to request rendering mode for the entire application (in onCreate() of your Activity, if you use the single-activity approach).

In _Jetpack Compose_, this step is no different.

**Eliminate visual conflicts**

If you follow the previous steps and run the application, you can see that the system no longer accounts for the space for the system UI. Now we have to do it ourselves:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qqjx8or5lvzqjbrb29l.jpg)

Android applications use [WindowInsets](https://developer.android.com/reference/android/view/WindowInsets) to manage the system UI. _Insets_ is an object that represents an area of a window that conflicts with the application. Conflicts can be different, and there’re different types of insets for that (gesture handling areas, system panels, bangs, etc.).

The [WindowInsetsCompat](https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat) class with backward compatibility and convenient separation into insets types is used for insets processing.

The processing itself consists of attaching a listener to the _View_, to which the system passes an _insets_ object. After you get the object, you can apply the desired padding or margin to the _View_:

```

ViewCompat.setOnApplyWindowInsetsListener(navBar) { view, insets ->
    val systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    view.updatePadding(
        bottom = systemBarInsets.bottom,
        left = systemBarInsets.left,
        right = systemBarInsets.right
    )
    insets
}
```
In KTS, we use the [insetter](https://github.com/chrisbanes/insetter) library, which works on the basis of this approach. The library implements a handy Kotlin DSL applyInsetter to handle _insets_.

```
toolbar.applyInsetter {
    type(navigationBars = true, statusBars = true) {
        padding(horizontal = true, top = true)
    }
}
```
_Jetpack Compose_ used to use the library from the [accompanist](https://github.com/google/accompanist/tree/main/insets) repository to install instes, but it’s obsolete now that official support for _insets_ is available in _Compose 1.2.0._ If you were already using [accompanist](https://github.com/google/accompanist/tree/main/insets), there’s a detailed migration guide on the website.

```
TopAppBar( 
  contentPadding = WindowInsets.systemBars
        .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top) 
        .asPaddingValues(), 
  backgroundColor = MaterialTheme.colors.primary 
) { 
  // content… 
}

```
**WindowInsets vs fitSystemWindow?**

Android has the _fitSystemWindow_ flag. If you set it to “true”, this flag adds padding to the container for which you specified the flag.

_FitsSystemWindows_ confuses many developers.

For example, this flag works with CoordinatorLayout and doesn’t work for FrameLayout. FitsSystemWindows = true doesn’t move your content under the status bar, but it works for layouts like CoordinatorLayout and DrawerLayout because they override the default behavior. Under the hood, they set the flags setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) if fitsSystemWindows is _true_.

- [SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN](https://developer.android.com/reference/android/view/View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) moves the content under the status bar.
- [SYSTEM_UI_FLAG_LAYOUT_STABLE](https://developer.android.com/reference/android/view/View#SYSTEM_UI_FLAG_LAYOUT_STABLE) ensures that the maximum possible system insets are applied, even if the current ones are less than this.

If we set these flags to our rootLayout, everything should work even on FrameLayout.

Also, hierarchy is important when using _fitSystemWindow_: if any parent has set this flag to “true”, its propagation won’t be taken into account further, because the container has already applied the indents.

This is actually not all the nuances why using the _fitSystemWindow_ flag isn’t recommended, so better use WindowInsets to avoid different problems and non-obvious behavior.

**What’s next?**

That’s all for now, but stay tuned ✌️
In the next part we will look at examples of processing different types of inserts:

- System Window Insets
- Ime Insets (Keyboard Processing)
- Stable Insets
- Immersive mode (full screen mode without UI elements)
- To hide the UI
- To show the UI
- Display Cutouts (Display cutout support)
- System Gesture Insets
- Mandatory System Gesture Insets
- Tappable element insets

See you later!

```

```