uragiristereo
uragiristereo

Reputation: 874

What is the proper way to get status bar height in compose?

Usually when using Accompanist Modifier.statusBarsHeight() the height will change depends on the status bar visibility, if it's visible either 24.dp or more and if it's invisible the height will be 0.dp. But i want the height won't change to zero regardless of its visibility.

I've been using this for a while:

// TODO: use better solution to get a fixed status bar height
val statusBarHeight = with (LocalDensity.current) { LocalWindowInsets.current.statusBars.top.toDp() }
val fixedStatusBarHeight = remember { statusBarHeight }

Upvotes: 19

Views: 32886

Answers (7)

Bram Jongebloet
Bram Jongebloet

Reputation: 336

To get the statusbarheight or inset for compose you need to do the following.

Step 1: include the following in the oncreate of you activity

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, false)
}

Step 2: After you included the setDecorFitsSystemWindows into your activity onCreate you need to add the following to the manifest of your activity.

android:windowSoftInputMode="adjustResize"

Step 3: To add the statusbar padding to your composable you can use the following modifier:

modifier = modifier.windowInsetsPadding(WindowInsets.safeDrawing)

Or if you want to use any other insetPaddings you can use the WindowInsets class. I've read a lot a issues on stackoverflow of people getting 0.0dp returned as there padding inset. That's because you forgot the setup part in step 1 and 2.

Hope this helps :)

Upvotes: 0

midFang
midFang

Reputation: 135

Here's how I set it, I get the height of the status bar from Resources

fun getStatusBarHeight(): Int {
    val resources = Resources.getSystem()
    val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
    return resources.getDimensionPixelSize(resourceId)
}

@Composable
fun Modifier.paddingBarHeight() = composed {
    padding(top = remember { getStatusBarHeight() })
}

Upvotes: 1

user924
user924

Reputation: 12293

You can use WindowInsets.statusBars.getTop(this)

For example (map takes all screen - behind status and navigation bar):

GoogleMap(
    modifier = Modifier.fillMaxSize(),
    cameraPositionState = cameraPositionState,
    properties = MapProperties(isMyLocationEnabled = true),
    contentPadding = with(LocalDensity.current) {
        PaddingValues(
            top = WindowInsets.statusBars.getTop(this).toDp(),
            bottom = WindowInsets.navigationBars.getBottom(this).toDp()
        )
    }
) {

Result:

enter image description here

Upvotes: 8

Mr. Techie
Mr. Techie

Reputation: 886

For TopAppBar, the following code works for getting its height:

import androidx.compose.runtime.*

...
...

@Composable
fun TopAppBarComposable(){
    var topAppBarHeight by remember {mutableStateOf(0.dp)}
    val localDensity = LocalDensity.current
    TopAppBar(
        modifier = Modifier
               .onGloballyPositioned { layoutCoordinates ->
                    with(localDensity){
                        topAppBarHeight = layoutCoordinates.size.height.toDp()
                    }
               }
    )

Upvotes: 1

Đốc.tc
Đốc.tc

Reputation: 923

The problem is getting complicated you should use it like this

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .statusBarsPadding().navigationBarsPadding()
            ) {

            }

            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .statusBarsPadding().systemBarsPadding()
            ) {

            }
        }
    }
}

Upvotes: 47

Joel Alvarado
Joel Alvarado

Reputation: 96

Currently, there is an ExperimentalLayoutApi that provides statusBarsIgnoringVisibility.

Example use case:

I have two screens, A and B. Screen A uses statusBarsIgnoringVisibility for top padding. In screen B, you can hide the status bar (e.g., full screen photo). When going from screen B to screen A, I want the top padding in screen A to already equal the full height of the status bar while the status bar comes back into view. Without statusBarsIgnoringVisibility, this transition doesn't look very good.

Upvotes: 7

uragiristereo
uragiristereo

Reputation: 874

So i ended up making a workaround alongside with a modifier extension for easier use.

First, make a data class to hold the fixed inset values.

data class FixedInsets(
    val statusBarHeight: Dp = 0.dp,
    val navigationBarsPadding: PaddingValues = PaddingValues(),
)

Second, create the extension and a composition local provider.

val LocalFixedInsets = compositionLocalOf<FixedInsets> { error("no FixedInsets provided!") }

@Composable
fun Modifier.fixedStatusBarsPadding(): Modifier {
    return this.padding(top = LocalFixedInsets.current.statusBarHeight)
}

@Composable
fun Modifier.fixedNavigationBarsPadding(): Modifier {
    return this.padding(paddingValues = LocalFixedInsets.current.navigationBarsPadding)
}

Finally, provide the fixed inset values at the very root of your composable function.

val systemBarsPadding = WindowInsets.systemBars.asPaddingValues()
val fixedInsets = remember {
    FixedInsets(
        statusBarHeight = systemBarsPadding.calculateTopPadding(),
        navigationBarsPadding = PaddingValues(
            bottom = systemBarsPadding.calculateBottomPadding(),
            start = systemBarsPadding.calculateStartPadding(LayoutDirection.Ltr),
            end = systemBarsPadding.calculateEndPadding(LayoutDirection.Ltr),
        ),
    )
}

CompositionLocalProvider(
    values = arrayOf(
        LocalFixedInsets provides fixedInsets,
    ),
) {
    MainScreen()
}

Navigation bar padding example:

Text(
    text = "Hello i'm a text with navigation bar padding",
    modifier = Modifier
        .fillMaxSize()
        .background(color = Color.Red)
        .fixedNavigationBarsPadding(),
)

Or you can access the sizes in Dp directly from the local composition by using calculateXPadding():

val fixedInsets = LocalFixedInsets.current

Text(
    text = "The navigation bar's height is: ${fixedInsets.navigationBarsPadding.calculateBottomPadding()}",
)

Upvotes: 4

Related Questions