diff --git a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt index 19ccc658..1a104283 100644 --- a/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt +++ b/feature/mutual/src/main/java/com/rtbishop/look4sat/feature/mutual/MutualScreen.kt @@ -54,11 +54,13 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -220,8 +222,18 @@ private fun MutualContent( } } + // Set once the list has actually been laid out. The prefill scroll only + // fires after this, because on a real device the Mutual entry appears + // through the NavDisplay transition and LaunchedEffect alone can run + // before the list is measurable (the scroll then never lands and the + // page stays at the top). onGloballyPositioned fires on the first real + // layout, so the subsequent scrollToItem always has a measured list. + var listReady by remember { mutableStateOf(false) } + LazyColumn( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .onGloballyPositioned { listReady = true }, state = listState, verticalArrangement = Arrangement.spacedBy(6.dp) ) { @@ -377,8 +389,8 @@ private fun MutualContent( // LazyListState at index 1: the constructor parameter is ignored by this // Compose version (verified in tests), while scrollToItem lands correctly. val matchSearchIndex = if (state.errorMessage != null) 2 else 1 - LaunchedEffect(state.scrollToTimeRange, matchSearchIndex) { - if (state.scrollToTimeRange) { + LaunchedEffect(state.scrollToTimeRange, matchSearchIndex, listReady) { + if (state.scrollToTimeRange && listReady) { listState.scrollToItem(matchSearchIndex) viewModel.consumeScrollToTimeRange() }