mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
feat(mutual): keep pass list scroll position across page switches
The mutual pass list LazyColumn reset to the top on every return to the page. Scroll position is now stored in the Activity-scoped MutualViewModel and restored on re-entry; a new query (queryGeneration bump) rebuilds the list at the top so stale positions never leak into fresh results.
This commit is contained in:
1 parent
4dbe896124
commit
3cd5b49f98
2 files changed
+40
No files matched your search
@@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
@@ -49,11 +50,13 @@ import androidx.compose.material3.Slider
|
|||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableFloatStateOf
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
@@ -74,6 +77,7 @@ import java.text.SimpleDateFormat
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MutualScreen(
|
fun MutualScreen(
|
||||||
@@ -150,6 +154,7 @@ fun MutualScreen(
|
|||||||
}
|
}
|
||||||
) { isVertical ->
|
) { isVertical ->
|
||||||
MutualContent(
|
MutualContent(
|
||||||
|
viewModel = viewModel,
|
||||||
state = state,
|
state = state,
|
||||||
isVertical = isVertical,
|
isVertical = isVertical,
|
||||||
onQuery = viewModel::queryMutualPasses,
|
onQuery = viewModel::queryMutualPasses,
|
||||||
@@ -172,6 +177,7 @@ fun MutualScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MutualContent(
|
private fun MutualContent(
|
||||||
|
viewModel: MutualViewModel,
|
||||||
state: MutualUiState,
|
state: MutualUiState,
|
||||||
isVertical: Boolean,
|
isVertical: Boolean,
|
||||||
onQuery: () -> Unit,
|
onQuery: () -> Unit,
|
||||||
@@ -195,8 +201,28 @@ private fun MutualContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List state survives page switches via the Activity-scoped ViewModel: the
|
||||||
|
// initial position is restored from the VM, and scrolling writes back to it,
|
||||||
|
// so returning to this page keeps exactly where the user left off. A new
|
||||||
|
// query (queryGeneration bump) rebuilds the state at the top.
|
||||||
|
val listState = remember(viewModel.queryGeneration) {
|
||||||
|
LazyListState(
|
||||||
|
firstVisibleItemIndex = viewModel.listScrollIndex,
|
||||||
|
firstVisibleItemScrollOffset = viewModel.listScrollOffset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
LaunchedEffect(listState) {
|
||||||
|
snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.collect { (index, offset) ->
|
||||||
|
viewModel.listScrollIndex = index
|
||||||
|
viewModel.listScrollOffset = offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
state = listState,
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
) {
|
) {
|
||||||
// Error message
|
// Error message
|
||||||
|
|||||||
@@ -69,6 +69,15 @@ class MutualViewModel(
|
|||||||
private val _uiState = MutableStateFlow(MutualUiState())
|
private val _uiState = MutableStateFlow(MutualUiState())
|
||||||
val uiState: StateFlow<MutualUiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<MutualUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
|
// Session-scoped scroll position of the pass list (Activity-scoped VM, so it
|
||||||
|
// survives switching to another page and back). Reset to the top whenever a
|
||||||
|
// new query replaces the results.
|
||||||
|
var listScrollIndex: Int = 0
|
||||||
|
var listScrollOffset: Int = 0
|
||||||
|
|
||||||
|
/** Bumped on every new query so the list scrolls back to the top. */
|
||||||
|
var queryGeneration: Int = 0
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Pre-fill station A with the user's current station position (as grid),
|
// Pre-fill station A with the user's current station position (as grid),
|
||||||
// and default min elevation to the same value used by the main radar passes
|
// and default min elevation to the same value used by the main radar passes
|
||||||
@@ -201,6 +210,11 @@ class MutualViewModel(
|
|||||||
selectedPassIndex = -1
|
selectedPassIndex = -1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// The results list is about to be replaced, so the scroll position must
|
||||||
|
// not leak from the previous query's list.
|
||||||
|
listScrollIndex = 0
|
||||||
|
listScrollOffset = 0
|
||||||
|
queryGeneration += 1
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val time = System.currentTimeMillis()
|
val time = System.currentTimeMillis()
|
||||||
|
|||||||
Reference in new issue
Block a user