Ui 从数据库中使用 Room 读取值时闪烁。

huangapple go评论63阅读模式
英文:

Ui flickering when reading value from database using room

问题

ViewModel

class FlashcardViewModel(private val flashcardsRepository: FlashcardsRepository): ViewModel() {
    
    val timeoutMillis = 5_000L

    fun getFlashcardsFilteredByTopic(topic: String): StateFlow<List<Flashcard>> {
        val flashcardUiState: StateFlow<List<Flashcard>> = flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(timeoutMillis),
                initialValue = listOf(Flashcard())
            )
        return flashcardUiState
    }
}

用于显示闪卡的可组合函数

@Composable
fun FlashcardScreen(
    topic: String
) {
    val viewModel: FlashcardViewModel = viewModel(factory = Factory)

    val flashcardUiState by viewModel.getFlashcardsFilteredByTopic(topic).collectAsState()

    var currentIndex by rememberSaveable{ mutableStateOf(0) }
    val currentFlashcard = flashcardUiState[currentIndex]

    Flashcard(flashcard = currentFlashcard)
}

@Composable
fun Flashcard(flashcard: Flashcard) {

        Text(
            text = flashcard.question
        )

        Text(
            text = flashcard.answer,
        )

}
英文:

My code is supposed to use room to get flashcards filtered by topic and then display it. The flashcard does get displayed but the text keeps on flickering. Any help would be appreciated.

ViewModel

class FlashcardViewModel(private val flashcardsRepository: FlashcardsRepository): ViewModel() {
    
    val timeoutMillis = 5_000L

    fun getFlashcardsFilteredByTopic(topic: String): StateFlow&lt;List&lt;Flashcard&gt;&gt; {
        val flashcardUiState: StateFlow&lt;List&lt;Flashcard&gt;&gt; = flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(timeoutMillis),
                initialValue = listOf(Flashcard())
            )
        return flashcardUiState
    }
}

Composable functions to display the flashcard

@Composable
fun FlashcardScreen(
    topic: String
) {
    val viewModel: FlashcardViewModel = viewModel(factory = Factory)

    val flashcardUiState by viewModel.getFlashcardsFilteredByTopic(topic).collectAsState()

    var currentIndex by rememberSaveable{ mutableStateOf(0) }
    val currentFlashcard = flashcardUiState[currentIndex]

    Flashcard(flashcard = currentFlashcard)
    
}

@Composable
fun Flashcard(flashcard: Flashcard) {

        Text(
            text = flashcard.question
        )

        Text(
            text = flashcard.answer,
        )

}

答案1

得分: 2

你在每次重新组合时都在调用 getFlashcardsFilteredByTopic。该函数会创建一个新的 StateFlow,其默认值是一个空卡片(listOf(Flashcard())),并且可能会执行数据库查询并使用结果更新流,从而触发重新组合并重新开始整个过程。

一个可能的解决方案是将 topic 放在 ViewModel 中,然后像这样做:

// FlashcardViewModel
val topic = MutableStateFlow("")
val flashCards = topic.flatMapLatest { topic ->
    flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
}.stateIn(...)

或者你可以在 ViewModel 中使用 remember 来记住你创建的流,这样只有当主题发生变化时才会调用 getFlashcardsFilteredByTopic

// FlashcardScreen
val flashCardsFlow = remember(topic) {
    viewModel.getFlashcardsFilteredByTopic(topic)
}
val flashcardUiState by flashCardsFlow.collectAsState()
英文:

You are calling getFlashcardsFilteredByTopic in each recomposition. This function creates new StateFlow with default value of empty card (listOf(Flashcard())) and presumably executes the database query and updates the flow with the result, which triggers the recomposition and it all starts again.

One possible solution would be to put the topic in the ViewModel and do something like this:

// FlashcardViewModel
val topic = MutableStateFlow(&quot;&quot;)
val flashCards = topic.flatMapLatest { topic -&gt;
    flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
}.stateIn(...)

Or you can remember the flow you create in ViewModel, this will only call getFlashcardsFilteredByTopic when the topic changes:

// FlashcardScreen
val flashCardsFlow = remember(topic) {
    viewModel.getFlashcardsFilteredByTopic(topic)
}
val flashcardUiState by flashCardsFlow.collectAsState()

huangapple
  • 本文由 发表于 2023年6月19日 03:18:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76502180.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定