Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why in HorizontalPager is the page number returned by the lambda different from the currentPage?

I am using this component and I was having a problem, as I don't get the same result like this:

HorizontalPager(count = tabTitles.size, state = pageState) { page ->
     Log.i("Polaris", "Hi, I'm page $page")
     Log.i("Polaris", "Hi, I'm currentPage $currentPage")
}

The result I get is:

2022-02-05 17:16:06.461 31070-31070/com.polaris I/Polaris: Hi, I'm page 0
2022-02-05 17:16:06.461 31070-31070/com.polaris I/Polaris: Hi, I'm currentPage 0
2022-02-05 17:16:06.464 31070-31070/com.polaris I/Polaris: Hi, I'm page 1
2022-02-05 17:16:06.464 31070-31070/com.polaris I/Polaris: Hi, I'm currentPage 0

Why does currentPage always return me the current page I'm on and page returns me the current and next page?

I edit the publication to add the rest of the code. The problem is that given the result that the page parameter gives me, the call that I have inside the Prediction component, when going from one page to another, replicates the same information that I get from the service in each one, instead of putting the one that corresponds to each one:

@ExperimentalPagerApi
@Composable
fun Tabs(zodiacName: String?, modifier: Modifier = Modifier) {
    val viewModel = getViewModel<DetailViewModel>()
    val tabTitles = listOf("Yesterday", "Today", "Tomorrow")
    val coroutineScope = rememberCoroutineScope()
    val pageState = rememberPagerState()
    Column(
        modifier
    ) {
        TabRow(
            modifier = Modifier.fillMaxWidth(),
            selectedTabIndex = pageState.currentPage,
            backgroundColor = Color.Transparent,
            indicator = { tabPositions ->
                TabRowDefaults.Indicator(
                    color = Color.White,
                    modifier = Modifier.pagerTabIndicatorOffset(
                        pageState,
                        tabPositions
                    ),
                    height = 2.dp
                )
            }) {
            tabTitles.forEachIndexed { index, title ->
                Tab(selected = pageState.currentPage == index,
                    onClick = {
                        coroutineScope.launch {
                            pageState.animateScrollToPage(index)
                        }
                    },
                    text = {
                        Text(
                            text = title,
                            color = Color.White,
                            fontSize = 14.sp,
                            fontFamily = Helvetica
                        )
                    })
            }
        }
        HorizontalPager(count = tabTitles.size, state = pageState) {
            when (currentPage) {
                0 -> Prediction(
                    viewModel = viewModel,
                    zodiacName = zodiacName,
                    day = "yesterday"
                )
                1 -> Prediction(
                    viewModel = viewModel,
                    zodiacName = zodiacName,
                    day = "today"
                )
                2 -> Prediction(
                    viewModel = viewModel,
                    zodiacName = zodiacName,
                    day = "tomorrow"
                )
            }
        }
    }
}

The content of the Prediction component is as follows:

@Composable
fun Prediction(viewModel: DetailViewModel, zodiacName: String?, day: String?) {
    val errorMessage = viewModel.errorMessage.value
    val horoscope = viewModel.horoscopeResponse.value
    if (errorMessage.isEmpty()) {
        LazyColumn(
            modifier = Modifier
                .padding(top = 20.dp, bottom = 10.dp, start = 10.dp, end = 10.dp)
        ) {
            item {
                LaunchedEffect(Unit) {
                    viewModel.getHoroscopeDetail(zodiacName, day)
                }
                if (horoscope.checkDescriptionContent()) {
                    PredictionCard(horoscope = horoscope)

                } else {
                    ShimmerCard()
                }
            }
        }

    } else {
        ErrorComponent(
            viewModel = viewModel,
            sign = zodiacName,
            day = day
        )
    }
}

Why then if I use currentPage it works fine, it makes the calls and places all the information correctly, but if I use the page parameter contained in the lambda, it duplicates the information?

like image 827
Ludiras Avatar asked Nov 01 '25 17:11

Ludiras


1 Answers

The HorizontalPager pre-draws a pair of adjacent pages, so that when you start scrolling, you don't have to wait for rendering.

This is exactly what happens in your logs: content is called for the first page and for the second, but currentPage is 0 in both cases.

You must always use the page parameter when creating a view within content, otherwise the view will be built on the wrong value during scrolling. When scrolling is over, the view will be recomposed with the new currentPage value, so it may look fine when scrolling fast, but if you scroll slowly, you will se the problem.

You can easily see how this works in the following example. See how I first scroll through the page without releasing my finger and currentPage shows the "wrong" number, which is updated when I release my finger and the animation ends.

HorizontalPager(
    count = 10,
) { page ->
    Column {
        Text("page $page")
        Text("currentPage $currentPage")
    }
}

like image 60
Philip Dukhov Avatar answered Nov 04 '25 06:11

Philip Dukhov