VerifyScreen: pin scroll shadows to divider edges

Top shadow: 16dp box immediately below the top divider, drawn as a
downward gradient. Appears when scrolled past first item.
Bottom shadow: 16dp box immediately above the bottom divider, drawn as
an upward gradient. Disappears when last item is visible.
Content scrolls under both edges — shadows are structural, not floating.
This commit is contained in:
Kyle Bolen
2026-07-28 05:40:22 +00:00
parent 7aa7e4dae1
commit 0be43f2236

View File

@@ -76,15 +76,16 @@ fun VerifyScreen(state: AppState) {
Spacer(Modifier.height(16.dp))
Text("Results", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
Spacer(Modifier.height(8.dp))
Spacer(Modifier.height(4.dp))
HorizontalDivider()
// Scrollable results list with top + bottom shadows
// Scrollable results list — shadows are pinned to the divider edges
val listState = rememberLazyListState()
val topShadowAlpha by remember {
derivedStateOf {
val scrolled = listState.firstVisibleItemScrollOffset +
listState.firstVisibleItemIndex * 80 // approximate
listState.firstVisibleItemIndex * 80
(scrolled / 60f).coerceIn(0f, 1f)
}
}
@@ -93,56 +94,61 @@ fun VerifyScreen(state: AppState) {
val info = listState.layoutInfo
val lastVisible = info.visibleItemsInfo.lastOrNull()
val total = info.totalItemsCount
if (lastVisible == null || lastVisible.index >= total - 1) 0f
else 1f // snap off at bottom is fine here since it's item-based not pixel
if (lastVisible == null || lastVisible.index >= total - 1) 0f else 1f
}
}
// Top edge: shadow drawn below the divider line above the list
if (topShadowAlpha > 0f) {
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.height(16.dp)
.drawWithContent {
drawContent()
// Top shadow — appears when scrolled down
if (topShadowAlpha > 0f) {
drawRect(
brush = Brush.verticalGradient(
colors = listOf(
Color.Black.copy(alpha = 0.08f * topShadowAlpha),
Color.Black.copy(alpha = 0.10f * topShadowAlpha),
Color.Transparent,
),
startY = 0f,
endY = 24.dp.toPx(),
)
)
}
// Bottom shadow — fades as last item approaches view
if (bottomShadowAlpha > 0f) {
drawRect(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.08f * bottomShadowAlpha),
),
startY = size.height - 24.dp.toPx(),
endY = size.height,
)
)
}
}
) {
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp),
contentPadding = PaddingValues(vertical = 4.dp),
) {
items(r.results, key = { it.photo.devicePath }) { copyResult ->
CopyResultRow(copyResult)
}
}
// Bottom edge: shadow drawn above the divider line below the list
if (bottomShadowAlpha > 0f) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(16.dp)
.drawWithContent {
drawContent()
drawRect(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.10f * bottomShadowAlpha),
),
)
)
}
)
}
Spacer(Modifier.height(16.dp))
HorizontalDivider()
Spacer(Modifier.height(12.dp))