最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Android Compose UiState not updated - Stack Overflow

programmeradmin2浏览0评论

I have a problem with Android Compose. It does not track the state of some variables as I would expect it too. Basically it does not track at all or sometimes not in real time what changed. I do the following:

In my NavHost or better said before I define the uiState by remember:

val uiState by viewModel.uiState.collectAsState()

NavHost(
    navController = navController,
    startDestination = AppMenu.Dex.name,
    modifier = modifier,
){
    composable(route = AppMenu.Dex.name) {
        DexScreen(
            viewModel = viewModel,
        )
    }
    composable(route = AppMenu.Statistics.name) {
        StatisticsScreen(
            dexUiState = uiState,
            viewModel = viewModel
        )
    }

After that I have in my StatisticsScrren:

    fun StatisticsScreen(
    dexUiState: DexUiState,
    viewModel: CollectionViewModel,
    modifier: Modifier = Modifier,
) {
    val uiState by viewModel.uiState.collectAsState()
    LazyColumn(modifier = modifier) {
        items(uiState.packs) { pack ->
            PackStatistics(pack)
        }
    }

}


@Composable
fun PackStatistics(
    pack: PackEntry,
    modifier: Modifier = Modifier,
) {
    val diamond:String = pack.collectedDiamond.toString() + "/" + pack.totalDiamond.toString()
    val star:String = pack.collectedStar.toString() + "/" + pack.totalStar.toString()

    Column {
        Text(stringResource(pack.packId))
        Row {
            Spacer(modifier = modifier.weight(1f))
            Icon(
                imageVector = SuitDiamond,
                contentDescription = null,
                modifier.padding(end = 3.dp, top = 4.dp)
            )
            Text(
                text = diamond
            )
            Icon(
                imageVector = Icons.Default.Star,
                contentDescription = null,
                modifier.padding(start = 10.dp, end = 3.dp)
            )
            Text(
                text = star
            )
        }
    }
}

So I take the values of uiState (I tried it either by defining the uiState in the StatisticsScreen as well as in the NavHost before, but it doesnt change anything.

I update the state in the ViewModel defined as follows:

private const val TAG: String = "DexScreenViewModel"

class CollectionViewModel(
    private val saveStateRepo: SaveStateRepo,
    private val settingsRepo: SettingsRepository
): ViewModel() {
    private val _uiState = MutableStateFlow((DexUiState()))

    val uiState: StateFlow<DexUiState> =
        combine(_uiState, settingsRepo.dexWidthSetting) {uiState, dexColumns ->
            uiState.copy(dexColumns = dexColumns)
        }.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(1),
            initialValue = DexUiState()
        )

    private fun load() {

        runBlocking {
            Log.d(TAG, "Start Settings load")
            val settingDexWidth = settingsRepo.dexWidthSetting.first()
            _uiState.update { currentState ->
                currentState.copy( dexColumns = settingDexWidth)
            }
            _uiState.value = _uiState.value.copy(dexColumns = settingDexWidth)
            Log.d(TAG, "Finished Settings load")
        }

        Log.d(TAG, "Started loading")
        viewModelScope.launch {
            Log.d(TAG, "Launched loading")
            saveStateRepo.getAllSaved()
                .collect{ savedDexEntry ->
                    val dbSavedMap = savedDexEntry.associateBy {it.cardId}

                    val updatedDex = Dex().loadDex()
                        .map { dexEntry: DexEntry ->
                            dbSavedMap[dexEntry.cardId]?.let { dbCard ->
                                dexEntry.copy(numberPossession = dbCard.inPossession)
                            } ?: dexEntry
                        }
                    _uiState.value = DexUiState(
                        packs = Packs().loadPacks().toMutableList(),
                        dex = updatedDex.toMutableList(),
                        dexColumns = 3,
                    )

                    val newDexList : MutableList<DexEntry> = mutableListOf()
                    for (dexEntry in _uiState.value.dex) {
                        //Log.d(TAG, "Starting searching Dex")
                        newDexList.add(
                            checkActive(dexEntry = dexEntry)
                        )
                    }

                    val newPackList: MutableList<PackEntry> = mutableListOf()
                    for (packEntry in _uiState.value.packs) {
                        newPackList.add(
                            calcRarity(packEntry = packEntry)
                        )
                    }
                    _uiState.update { currentState ->
                        currentState.copy(packs = newPackList, dex = newDexList)
                    }
                }
        }

        val newDexList : MutableList<DexEntry> = mutableListOf()
        for (dexEntry in _uiState.value.dex) {
            newDexList.add(
                checkActive(dexEntry = dexEntry)
            )
        }

        val newPackList: MutableList<PackEntry> = mutableListOf()
        for (packEntry in _uiState.value.packs) {
            newPackList.add(
                calcRarity(packEntry = packEntry)
            )
        }
        _uiState.update { currentState ->
            currentState.copy(packs = newPackList, dex = newDexList)
        }
        Log.d(TAG, "Finished loading")
    }

    private fun calcRarity(packEntry: PackEntry): PackEntry {
        val packId = packEntry.id
        var totalDiamonds = 0
        var collectedDiamonds = 0
        var totalStars = 0
        var collectedStars = 0
        for (card in _uiState.value.dex) {
            if (packId in card.packIds) {
                if (card.rarity >= 10) {
                    totalStars ++
                    if (card.isActive) {collectedStars ++}
                } else {
                    totalDiamonds ++
                    if (card.isActive) {collectedDiamonds ++}
                }
            }
        }
        packEntry.totalStar = totalStars
        packEntry.totalDiamond = totalDiamonds
        packEntry.collectedStar = collectedStars
        packEntry.collectedDiamond = collectedDiamonds
        Log.d(TAG, "Calculated rarity")
        return packEntry
        }

    private fun checkActive(dexEntry: DexEntry): DexEntry {
        return dexEntry.copy(isActive = dexEntry.numberPossession > 0)
    }

    fun toggleEntry(cardId: Int) {
        val newDex = _uiState.value.dex
        val cardIndex = newDex.indexOfFirst { it.cardId == cardId }  //-1 if no item was found

        if (cardIndex == -1) {
            return
        }

        val newCard = newDex[cardIndex]
        newCard.isActive = !newCard.isActive

        newDex[cardIndex] = newCard
        _uiState.update { currentState ->
            currentState.copy(dex = newDex)
        }
        Log.d(TAG, "Updated _uiState")

        val newPacks = _uiState.value.packs
        for (packId in newCard.packIds) {
            val packIndex = _uiState.value.packs.indexOfFirst { it.id == packId }
            if (packIndex == -1){
                Log.d(TAG, "Could not find Pack with ID: $packId")
            } else {
                newPacks[packIndex] = calcRarity(newPacks[packIndex])
            }
        }
        _uiState.update { currentState ->
            currentState.copy(packs = newPacks)
        }

    }

    init {
        load()
    }
}

In the Logs can see that calcRarity() is called, so in toggle Entry the Packs should be updated. But I can not see any updates on the screen.toggle Entry is called from another Screen (DexScreen). On this Screen the values of packEntry.collectedStar and packEntry.collectedDiamond is also not updated.

If you need the whole code, you can find it here. I tried it with diffrent methods like passing through the viewModel or calling by remember or assigning the variable by collect as StateFlow, but nothing seems to work.

Does anybody have an idea, what went wrong? Its my first real Android Project and the smaller ones all worked as I expected.

Best regards

发布评论

评论列表(0)

  1. 暂无评论