Swiping don't work properly in Horizontal pager with scrollable tab row in compose

89 Views Asked by At

My horizontal pager doesn't work properly when adding some content to any screen. Here, my first screen contains a simple card with four PNG logos and some text headers. But it has a lag and a noticeable delay when scrolling by swiping or when a tab is clicked. Another related problem is that when swiping to the next one, I have to make a full-screen-width swipe, or it will step back to the same position.  The remaining screens have only one centered text for testing, but all tabs still have the following problem: (When swiping from screen to screen, the current screen content indeed appears on the other screen for a moment with not-fetched data before the new content data is successfully fetched.).

The app is now running in release mode, with R8 enabled. With less delay, but the same problems remain.

The used implementation("androidx.compose.foundation.pager").

//Edit:
val pagerState = rememberPagerState(initialPage = 0, pageCount = { screens.size })
val pagerPage = rememberSaveable { mutableIntStateOf(0) }
    
LaunchedEffect(key1 = pagerState.currentPage ) {
    pagerPage.intValue = pagerState.currentPage
}

@Composable
fun CustomScrollableTabRow(
    tabs: List<TournamentScreens>,
    selectedTabIndex: Int,
    state: PagerState,
    onTabClick: (Int) -> Unit
) {
    val density = LocalDensity.current
    val tabWidths = remember {
        val tabWidthStateList = mutableStateListOf<Dp>()
        repeat(tabs.size) {
            tabWidthStateList.add(0.dp)
        }
        tabWidthStateList
    }
    ScrollableTabRow(
        selectedTabIndex = selectedTabIndex,
        contentColor = Color.White,
        edgePadding = 0.dp,
        indicator = { tabPositions ->
            SecondaryIndicator(
                modifier = Modifier.customTabIndicatorOffset(
                    currentTabPosition = tabPositions[selectedTabIndex],
                    tabWidth = tabWidths[selectedTabIndex]
                ),
                color = MaterialTheme.colorScheme.primary
            )
        }
    ) {
        tabs.forEachIndexed { tabIndex, tab ->
            Tab(
                selected = selectedTabIndex == tabIndex,
                onClick = { onTabClick(tabIndex) },
                text = {
                    Text(
                        text = tab.title,
                        onTextLayout = { textLayoutResult ->
                            tabWidths[tabIndex] =
                                with(density) { textLayoutResult.size.width.toDp() }
                        }
                    )
                }
            )
        }
    }
    HorizontalPager(
        outOfBoundsPageCount = tabs.size,
        state = state,
    ) {
        tabs[state.currentPage].screen()
    }
}

fun Modifier.customTabIndicatorOffset(
    currentTabPosition: TabPosition,
    tabWidth: Dp
): Modifier = composed(
    inspectorInfo = debugInspectorInfo {
        name = "customTabIndicatorOffset"
        value = currentTabPosition
    }
) {
    val currentTabWidth by animateDpAsState(
        targetValue = tabWidth,
        animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing), label = ""
    )
    val indicatorOffset by animateDpAsState(
        targetValue = ((currentTabPosition.left + currentTabPosition.right - tabWidth) / 2),
        animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing), label = ""
    )
    fillMaxWidth()
        .wrapContentSize(Alignment.BottomStart)
        .offset(x = indicatorOffset)
        .width(currentTabWidth)
}

I hope I gave a good explanation.

1

There are 1 best solutions below

2
LittlePony On

Your problem is that you are using a state.currentPager (PagerState). According to the documentation, it changes the content of the next screen after a certain offset of the current one. Therefore, it is better for you to use the index that HorizontalPager itself offers you

enter image description here

enter image description here