-
Notifications
You must be signed in to change notification settings - Fork 619
rememberPagerState doesn't allow a reset on pageCount changed #768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I think it is not a pattern we want to follow, none of our other rememberFoo() functions in Compose UI have the inputs param, which can feel confusing given that remember() and rememberSaveable() do have such param. However in reality this is added mostly for making the common use cases simpler, but in fact Also, getting back to your use case, are you sure you want to completely reset the state when the pageCount changes? Imagine the case where you had 10 items and was displaying the 5th item after scroll. Then a new item was added in the end so you now have 11 items, if you reset the state you will end up displaying the 1th item, didn't you want to continue displaying the 5th item? I think Pager state will be updated with the new pageCount once the next measurement happens. Can you maybe a share a whole sample which crashes? |
Hey, in advance, the solution Here is a simple example which results in the crash when you scroll to page > "Test2" and press the button. In my real world szenario i have a sreen, which shows multiple graphs (AndroidView+MPAndroidChart) for measurement values for a selected aquarium. You are able to select another aquarium in the same screen. The new one could possible have less graphs to show. I attached a screenshot of the real app. If i select the aquarium "Test Aquarium", the app crashes because the new aquarium only has one graph to show. Crash example: var listItems by remember { mutableStateOf(listOf("Test1", "Test2", "Test3", "Test4", "Test5")) }
val state = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
Surface(
modifier = Modifier.statusBarsPadding()
) {
Column(
modifier = Modifier.fillMaxSize()
) {
Button(
modifier = Modifier.padding(bottom = 10.dp),
onClick = {
listItems = listOf("Test New 1", "Test New 2")
}
) {
Text("Load another context")
}
ScrollableTabRow(
selectedTabIndex = state.currentPage,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
modifier = Modifier.pagerTabIndicatorOffset(state, tabPositions),
)
}
) {
// Add tabs for all of our pages
listItems.forEachIndexed { index, s ->
Tab(
text = {
Text(
text = s
)
},
selected = state.currentPage == index,
onClick = {
// Animate to the selected page when clicked
coroutineScope.launch {
state.animateScrollToPage(index)
}
}
)
}
}
HorizontalPager(
count = listItems.count(),
state = state,
) { page ->
Text(text = "Content for page: ${page}")
}
}
} The crash:
|
Thanks, we need to fix it in Modifier.pagerTabIndicatorOffset() to not crash in such situations. It is possible that the PagerState is not aware about the change in the data set so will have an incorrect index. We first need to use minOf(tabPositions.lastIndex, pagerState.currentPage); second to move the offset calculation out of the composition via using the other Modifier.offset() overload which accepts lambda |
I think the main reason for these issue is that Increase the item count -> index stays at the same. Decrease item count -> index decreased until the highest possible index. The |
Indeed, I agree it is wrong. pagerState.currentPage should be updated in this situation. We currently update this variable only after scroll finished, but we need to also update it when the items count changes |
Would be LaunchedEffect(count) {
state.currentPage = minOf(count-1, state.currentPage)
} the correct part within the Pager? |
Yes, it is one of the ways to solve that. Feel free to create a pull request |
Describe the bug
pagerTabIndicatorOffset
crashes when page count changes.Expected behavior
No crash should occur when page count changes
Additional context
rememberPagerState
should be recreated on data changed. Use of the missing paramterinputs
inrememberSavable
fixed the issue.inputs - A set of inputs such that, when any of them have changed, will cause the state to reset and init to be rerun
The text was updated successfully, but these errors were encountered: