How to implement something like `Modifier.navigati...
# compose
m
How to implement something like
Modifier.navigationBarsPadding()
but only apply the bottom padding (i.e. it would do nothing if no nav bar at the bottom)? i.e. I want to force the 3 other paddings to zero.
c
Isn't that what
navigationBarsPadding()
already does?
c
Yeah, as someone thats used the insets apis a ton... this seems like exactly what navBarPadding does?
m
I’m pretty sure
navigationBarsPadding
applies the padding according to where the nav bar is, e.g. on the right/left on a phone in landscape mode.
c
Ah!
c
oh. not sure in that case. maybe @Alex Vanyo?
just out of curiosity. why don't you want to apply the nav bar padding in that case?
m
My use case is that I want to draw under the nav bar only when it’s at the bottom. It’s too complex and not particular useful, in my case, to handle the situation drawing under left/right nav bars. So I handle them differently.
My app is entirely vertical-scrolling content, so it makes more sense in the nav-bar-bottom scenario
c
You might be able to conditionally apply
navigationBarsPadding()
only if
LocalConfiguration.current
tells you that you are in portrait mode. But I'm curious if there is a better, more idiomatic way.
m
Unfortunately that’s not good enough, because tablets have nav bar at the bottom in landscape mode.
There are other ways, but seems very non-composably:
Copy code
val insets = activity.getWindowManager().getCurrentWindowMetrics().getWindowInsets()
val navigationBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom //in pixels
o
Maybe?
Copy code
val density = LocalDensity.current
val bottomPx = WindowInsets.navigationBars.getBottom(density)
val bottom = LocalDensity.current.run { bottomPx.toDp() }
val padding = Modifier.padding(bottom = bottom)
Edit: read the real answer further in thread 👇 https://kotlinlang.slack.com/archives/CJLTWPH7S/p1682608271225589?thread_ts=1682599551.610209&cid=CJLTWPH7S
m
Thanks @Oleksandr Balan, at first try it seems to work perfectly, though I need to do some more testing. Is this the right implementation?
Copy code
fun Modifier.navigationBarsPaddingBottom(): Modifier = composed {
    val density = LocalDensity.current
    val bottomPx = WindowInsets.navigationBars.getBottom(density)
    val bottom = density.run { bottomPx.toDp() }
    this.padding(bottom = bottom)
}
o
Looks fine to me 👌
a
You can use
Modifier.windowInsetsPadding(WindowInsets.navigationBars.only(WindowInsetsSides.Bottom))
s
☝️ This is the answer. Here’s it in action (albeit using safeDrawing, but the same thing applies) https://github.com/HedvigInsurance/android/blob/79d60b0a7665c03581d1e9c5690d2d1594[…]com/hedvig/android/odyssey/step/location/LocationDestination.kt very specifically to solve the exact same issue, adding the correct amount of padding to the bottom of a scrollable list
m
Is there a downside in making an extension fun (wrapping it in a
compose { }
)?
Copy code
fun Modifier.navigationBarsPaddingBottom(): Modifier = composed {
    this.windowInsetsPadding(WindowInsets.navigationBars.only(WindowInsetsSides.Bottom))
}
a
No functional downside, that will be correct if you find it useful to make a shorter type-alias. So just a balance then of explictness versus length
s
I tend not to make such functions since then you forget that they exist or your colleagues never even know that they exist, while it's more likely that they'll know about the normal official APIs. And then you end up with a mix unless you very explicitly enforce using your function with a lint or something like that.
a
I agree, I personally don't think the size reduction there would be worth the additional overhead of having to wonder what this custom extension does. A lot of Compose code can be dense, in the sense that a lot of individual parts are all very necessary. Sometimes trying to extract that out into helper methods can just obscure what's going on more. It's always a balance depending on your preference, your team's preference and the codebase