admin管理员组

文章数量:1321253

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

本文标签: Android Compose UiState not updatedStack Overflow