fix(theme): inset-aware chrome compaction (no first-frame snap)
Previous attempt computed total chrome height as
`Modifier.height(content + insetReadViaPaddingValues)`. The inset is
read at composition time and arrives as 0 on the very first frame
before the system-bar inset connection delivers its value — the bar
laid out at the shorter (no-inset) height, then re-measured once the
inset arrived. Visible as a one-frame snap on app cold start.
Switch to a layout-time pattern that subscribes to inset changes
correctly:
Modifier
.windowInsetsPadding(insets) // reserves the inset via padding
.height(contentHeight) // content area only
`windowInsetsPadding` is a Modifier.Node that re-layouts (not re-
composes) on inset arrival, so the bar measures at the right total
height on the first frame. The bar's own `windowInsets` is set to
`WindowInsets(0)` so it doesn't double-pad.
Applied to:
- `BottomBar` NavigationBar — content height 56dp + navigation-bars inset
- `FeedScreen` CenterAlignedTopAppBar — content height 48dp + status-bars
inset
This commit is contained in:
@@ -19,8 +19,9 @@ import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBar
|
||||
@@ -81,14 +82,16 @@ fun WispBottomBar(
|
||||
// + chrome and reserves the lighter "surface" for elevated
|
||||
// controls (pills, cards).
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
// Material's default 80dp NavigationBar reserves a tall slot
|
||||
// for labels we never render — clamp to 56dp + the gesture
|
||||
// inset for a chrome height closer to the iOS tab bar.
|
||||
modifier = Modifier.height(
|
||||
56.dp + NavigationBarDefaults.windowInsets
|
||||
.asPaddingValues().calculateBottomPadding()
|
||||
),
|
||||
windowInsets = NavigationBarDefaults.windowInsets
|
||||
// Compact the chrome: reserve the gesture inset via
|
||||
// `windowInsetsPadding` (resolved at layout time, so no
|
||||
// first-frame snap) and clamp the content area to 56dp
|
||||
// (was Material's default 80dp). Pass `WindowInsets(0)` to
|
||||
// disable NavigationBar's internal inset handling so we
|
||||
// don't double-pad.
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(NavigationBarDefaults.windowInsets)
|
||||
.height(56.dp),
|
||||
windowInsets = WindowInsets(0)
|
||||
) {
|
||||
visibleTabs.forEach { tab ->
|
||||
val selected = currentRoute == tab.route
|
||||
|
||||
@@ -15,8 +15,8 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -752,14 +752,15 @@ fun FeedScreen(
|
||||
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||
topBar = {
|
||||
CenterAlignedTopAppBar(
|
||||
// Material's default 64dp content + status-bar inset
|
||||
// pads a chunky gap below the icon row. Clamp to 48dp
|
||||
// + status-bar inset so the chrome lands closer to
|
||||
// the iOS navigation bar (~44dp content + safe-area).
|
||||
modifier = Modifier.height(
|
||||
48.dp + WindowInsets.statusBars
|
||||
.asPaddingValues().calculateTopPadding()
|
||||
),
|
||||
// Compact: reserve the status-bar inset via
|
||||
// `windowInsetsPadding` (layout-time, no first-frame
|
||||
// snap) and clamp the content area to 48dp (was
|
||||
// Material's default 64dp). Disable the bar's
|
||||
// internal inset handling to avoid double-padding.
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.statusBars)
|
||||
.height(48.dp),
|
||||
windowInsets = WindowInsets(0),
|
||||
title = {
|
||||
Box {
|
||||
Surface(
|
||||
|
||||
Reference in New Issue
Block a user