Fix race condition in rememberPageStateFixed

During screen rotation, currentPage could be updated to a wrong value.

Split the rotation detection and scrolling to correct page to fix this
issue.

Fix: 281928661
Test: Manually with App languages page
Change-Id: I71f69db0c832dc4b4dc90ee210efbfe768cf17e4
This commit is contained in:
Chaohui Wang
2023-05-15 17:13:26 +08:00
parent d516a7caff
commit a030ec3227

View File

@@ -21,14 +21,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.TabRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
@@ -50,7 +49,7 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit
Column {
val coroutineScope = rememberCoroutineScope()
val pagerState by rememberPageStateFixed()
val pagerState = rememberPageStateFixed()
TabRow(
selectedTabIndex = pagerState.currentPage,
@@ -89,17 +88,30 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit
*/
@Composable
@OptIn(ExperimentalFoundationApi::class)
private fun rememberPageStateFixed(): State<PagerState> {
private fun rememberPageStateFixed(): PagerState {
var currentPage by rememberSaveable { mutableStateOf(0) }
val pagerStateHolder = remember { mutableStateOf(PagerState(currentPage)) }
LaunchedEffect(LocalConfiguration.current.orientation) {
var targetPage by rememberSaveable { mutableStateOf(-1) }
val pagerState = rememberPagerState()
val configuration = LocalConfiguration.current
var lastScreenWidthDp by rememberSaveable { mutableStateOf(-1) }
val screenWidthDp = configuration.screenWidthDp
LaunchedEffect(screenWidthDp) {
// Reset pager state to fix an issue after configuration change.
// When we declare android:configChanges="orientation" in the manifest, the pager state is
// in a weird state after configuration change.
pagerStateHolder.value = PagerState(currentPage)
// When we declare android:configChanges in the manifest, the pager state is in a weird
// state after configuration change.
targetPage = currentPage
lastScreenWidthDp = screenWidthDp
}
LaunchedEffect(targetPage) {
if (targetPage != -1) {
pagerState.scrollToPage(targetPage)
targetPage = -1
}
}
SideEffect {
currentPage = pagerStateHolder.value.currentPage
if (lastScreenWidthDp == screenWidthDp) {
currentPage = pagerState.currentPage
}
}
return pagerStateHolder
return pagerState
}