recalculateWindowInsets

Functions summary

Modifier

This recalculates the WindowInsets based on the size and position.

Cmn

Functions

Modifier.recalculateWindowInsets

fun Modifier.recalculateWindowInsets(): Modifier

This recalculates the WindowInsets based on the size and position. This only works when Constraints have fixed width and fixed height. This can be accomplished, for example, by having Modifier.size, or Modifier.fillMaxSize, or other size modifier before recalculateWindowInsets. If the Constraints sizes aren't fixed, recalculateWindowInsets won't adjust the WindowInsets and won't have any affect on layout.

recalculateWindowInsets is useful when the parent does not call consumeWindowInsets when it aligns a child. For example, a Column with two children should have different WindowInsets for each child. The top item should exclude insets below its bottom and the bottom item should exclude the top insets, but the Column can't assign different insets for different children.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.recalculateWindowInsets
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color

var hasFirstItem by remember { mutableStateOf(true) }
var hasLastItem by remember { mutableStateOf(true) }
Column(Modifier.fillMaxSize()) {
    if (hasFirstItem) {
        Box(Modifier.weight(1f).fillMaxWidth().background(Color.Magenta))
    }
    Box(
        Modifier.fillMaxWidth() // force a fixed size on the content
            .recalculateWindowInsets()
            .weight(1f)
            .background(Color.Yellow)
            .safeDrawingPadding()
    ) {
        Button(
            onClick = { hasFirstItem = !hasFirstItem },
            Modifier.align(Alignment.TopCenter),
        ) {
            val action = if (hasFirstItem) "Remove" else "Add"
            Text("$action First Item")
        }
        Button(
            onClick = { hasLastItem = !hasLastItem },
            Modifier.align(Alignment.BottomCenter),
        ) {
            val action = if (hasLastItem) "Remove" else "Add"
            Text("$action Last Item")
        }
    }
    if (hasLastItem) {
        Box(Modifier.weight(1f).fillMaxWidth().background(Color.Cyan))
    }
}

Another use is when a parent doesn't properly consumeWindowInsets for all space that it consumes. For example, a 3rd-party container has padding that doesn't properly use consumeWindowInsets.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.recalculateWindowInsets
import androidx.compose.foundation.layout.safeContent
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

// This outer Box is representing a 3rd-party layout that you don't control. It has a
// padding, but doesn't properly use consumeWindowInsets()
Box(Modifier.padding(10.dp)) {
    // This is the content that you control. You can make sure that the WindowInsets are correct
    // so you can pad your content despite the fact that the parent did not
    // consumeWindowInsets()
    Box(
        Modifier.fillMaxSize() // Force a fixed size on the content
            .recalculateWindowInsets()
            .safeContentPadding()
            .background(Color.Blue)
    )
}

In most cases you should not need to use this API, and the parent should instead use consumeWindowInsets to provide the correct values

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContent
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

// The outer Box uses padding and properly compensates for it by using consumeWindowInsets()
Box(
    Modifier.fillMaxSize()
        .padding(10.dp)
        .consumeWindowInsets(WindowInsets(10.dp, 10.dp, 10.dp, 10.dp))
) {
    Box(Modifier.fillMaxSize().safeContentPadding().background(Color.Blue))
}