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:
@@ -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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user