Home > Mobile >  Screen scrolls to the top (Jetpack Compose Pagination)
Screen scrolls to the top (Jetpack Compose Pagination)

Time:11-23

I am trying to do pagination in my application. First, I'm fetching 20 item from Api (limit) and every time i scroll down to the bottom of the screen, it increase this number by 20 (nextPage()). However, when this function is called, the screen goes to the top, but I want it to continue where it left off. How can I do that?

Here is my code:

CharacterListScreen:

@Composable
fun CharacterListScreen(
    characterListViewModel: CharacterListViewModel = hiltViewModel()
) {

    val state = characterListViewModel.state.value
    val limit = characterListViewModel.limit.value

    Box(modifier = Modifier.fillMaxSize()) {
        val listState = rememberLazyListState()

        LazyColumn(modifier = Modifier.fillMaxSize(), state = listState) {
            itemsIndexed(state.characters) { index, character ->
                characterListViewModel.onChangeRecipeScrollPosition(index)
                if ((index   1) >= limit) {
                    characterListViewModel.nextPage()
                }
                CharacterListItem(character = character)
            }
        }

        if (state.error.isNotBlank()) {
            Text(
                text = state.error,
                color = MaterialTheme.colors.error,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 20.dp)
                    .align(Alignment.Center)
            )
        }
        if (state.isLoading) {
            CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
        }
    }
}

CharacterListViewModel:

@HiltViewModel
class CharacterListViewModel @Inject constructor(
    private val characterRepository: CharacterRepository
) : ViewModel() {

    val state = mutableStateOf(CharacterListState())
    val limit = mutableStateOf(20)
    var recipeListScrollPosition = 0

    init {
        getCharacters(limit.value, Constants.HEADER)
    }

    private fun getCharacters(limit : Int, header : String) {
        characterRepository.getCharacters(limit, header).onEach { result ->
            when(result) {
                is Resource.Success -> {
                    state.value = CharacterListState(characters = result.data ?: emptyList())
                }
                is Resource.Error -> {
                    state.value = CharacterListState(error = result.message ?: "Unexpected Error")
                }
                is Resource.Loading -> {
                    state.value = CharacterListState(isLoading = true)
                }
            }
        }.launchIn(viewModelScope)
    }

    private fun incrementLimit() {
        limit.value = limit.value   20
    }

    fun onChangeRecipeScrollPosition(position: Int){
        recipeListScrollPosition = position
    }

    fun nextPage() {
            if((recipeListScrollPosition   1) >= limit.value) {
                incrementLimit()
                characterRepository.getCharacters(limit.value, Constants.HEADER).onEach {result ->
                    when(result) {
                        is Resource.Success -> {
                            state.value = CharacterListState(characters = result.data ?: emptyList())
                        }
                        is Resource.Error -> {
                            state.value = CharacterListState(error = result.message ?: "Unexpected Error")
                        }
                        is Resource.Loading -> {
                            state.value = CharacterListState(isLoading = true)
                        }
                    }
                }.launchIn(viewModelScope)
            }
    }

}

CharacterListState:

data class CharacterListState(
    val isLoading : Boolean = false,
    var characters : List<Character> = emptyList(),
    val error : String = ""
)

CodePudding user response:

I think the issue here is that you are creating CharacterListState(isLoading = true) while loading. This creates an object with empty list of elements. So compose renders an empty LazyColumn here which resets the scroll state. The easy solution for that could be state.value = state.value.copy(isLoading = true). Then, while loading, the item list can be preserved (and so is the scroll state)

CodePudding user response:

Not sure if you are using the LazyListState correctly. In your viewmodel, create an instance of LazyListState:

val lazyListState: LazyListState = LazyListState()

Pass that into your composable and use it as follows:

@Composable
fun CharacterListScreen(
    characterListViewModel: CharacterListViewModel = hiltViewModel()
) {

    val limit = characterListViewModel.limit.value

    Box(modifier = Modifier.fillMaxSize()) {
        
        LazyColumn(modifier = Modifier.fillMaxSize(), state = characterListViewModel.lazyListState) {
            itemsIndexed(state.characters) { index, character ->

            }
        }
    }
}
  • Related