srituts
srituts

Reputation: 63

Holding a List State in Horizontal Pager - Jetpack Compose

I am currently creating an app with Horizontal Pager in JetPack Compose as below

@ExperimentalAnimationApi
@ExperimentalPagerApi // HorizontalPager is experimental
@Composable
fun HorizontalPages(
    lstQuestions: List<CertQuestions>,
    lstOptions: List<CertAnswers>,
    mainDataViewModel: MainDataViewModel,
    pagerState: PagerState,
    modifier: Modifier = Modifier
) {
     HorizontalPager(
        state = pagerState,
        modifier = Modifier.fillMaxSize()
    ) { page ->
        val certQuestions = lstQuestions[page]
        val lstOptionsFiltered: List<CertAnswers> = lstOptions.filter { lstQuestions.map { certQuestions.question_id } .contains(it.question_id) }
        val lst: List<CertAnswers> =
            lstOptionsFiltered.filter { it.question_id == certQuestions.question_id }.toList()
            QueryCardPager(query = certQuestions, lstOptions = lst, mainDataViewModel= mainDataViewModel , pageNumber = page+1, pageCount = lstQuestions.size)
    }
}

The listQuestion in the above is basically a shuffled list after I retrieve it from DB based on an condition.

when (val result = mainDataViewModel.quState.collectAsState().value) {
    is QueryState.Error -> {
        Log.d("Error", "Error Occured")
    }
    is QueryState.Success -> {
        lstQuestions = result.queries

        lstQuestions = when (filter) {
            "pass" -> {
                lstQuestions.filter { it.knowAnswer }.shuffled()
            }
            "new" -> {
                lstQuestions.filter { !it.knowAnswer }.shuffled()
            }
            "random25" -> {
                lstQuestions.shuffled().take(25)
            }
            "random65" -> {
                lstQuestions.shuffled().take(65)
            }
            else -> {
                lstQuestions.shuffled()
            }
        }
    }
}

In the Card View,I am calling a ViewModel Data Update Method, which basically sends a updated boolean variable to DB.

@ExperimentalAnimationApi
@Composable
fun QueryCardPager(query: CertQuestions,
                   lstOptions: List<CertAnswers>,
                   mainDataViewModel: MainDataViewModel,
                   pageNumber : Int,
                   pageCount : Int,
){
.
.
.
    IconButton(
        onClick = {
            mainDataViewModel.updateKnowAnswer(query.question_id, !query.knowAnswer)
        }
    ) {
        Icon(
            imageVector = if (query.knowAnswer) Icons.Filled.DoneAll
            else Icons.Filled.Done,
            contentDescription = null,
            tint = if (query.knowAnswer) Color.Blue else Color.Gray
        )
    }
}

ViewModel

private val _quState = MutableStateFlow<QueryState>(QueryState.Loading)
private val _optState = MutableStateFlow<OptionsState>(OptionsState.Loading)

// UI collects from this StateFlow to get it"s state update
val quState = _quState.asStateFlow()
val optState = _optState.asStateFlow()

fun getQuestions() = viewModelScope.launch {

    try {
        certsRepository.getAllQuestions().distinctUntilChanged().collect(){ result->
            if(result.isEmpty()){
                _quState.value =QueryState.Empty
            }else
            {
                _quState.value = QueryState.Success(result)
                //questionsData.value = result
            }
        }
    }catch (e: Exception){
        _quState.value = QueryState.Error(exception = e)
    }
}

The problem is after I call the updateKnowAnswer method, it works for sometime. and when it work, the list is refreshed again and a new shuffled list is loaded. How to save the list and whether I am calling the update method in a right way.

Upvotes: 0

Views: 3153

Answers (1)

Phil Dukhov
Phil Dukhov

Reputation: 88072

You need to save ids after shuffle and reuse them on the next time. I suggest you moving this login to the view model too, like this:

private val _filter = MutableStateFlow("pass")
val filter: Flow<String> = _filter

fun updateFilter(filter: String) {
    _filter.value = filter
}

private var savedRandomIds = mutableMapOf<String, List<QueryId>>()

val lstQuestions: Flow<List<Query>> = quState.combine(_filter) { result, filter ->
    when (result) {
        is QueryState.Error -> {
            Log.d("Error", "Error Occured")
            listOf()
        }
        is QueryState.Success -> {
            val savedIds = savedRandomIds[filter]
            if (savedIds != null) {
                result.queries.filter { savedIds.contains(it.id) }
            } else {
                when (filter) {
                    "pass" -> {
                        result.queries
                            .filter { it.knowAnswer }
                            .shuffled()
                    }
                    "new" -> {
                        result.queries.filter { !it.knowAnswer }.shuffled()
                    }
                    "random25" -> {
                        result.queries.shuffled().take(25)
                    }
                    "random65" -> {
                        result.queries.shuffled().take(65)
                    }
                    else -> {
                        result.queries.shuffled()
                    }
                }.also { shuffled ->
                    savedRandomIds[filter] = shuffled.map { it.id }
                }
            }
        }
    }
}

In your composable collect lstQuestions instead of quState:

mainDataViewModel.lstQuestions.collectAsState(listOf())

Upvotes: 0

Related Questions