Estou usando uma consulta observável para observar alterações em um banco de dados Android Room e exibir os resultados em uma LazyColumn. Isso funciona bem, mas agora quero que o usuário consiga alterar a ordem de classificação. Com minha implementação atual, alterar a ordem de classificação não faz com que a LazyColumn se recomponha. Se outra parte do estado mudar, a LazyColumn se recomporá e a ordenação atualizada será refletida na visualização.
Meu DAO se parece com isso:
@Dao
interface EntityDao {
@Query("SELECT * FROM entity" +
" ORDER BY " +
" CASE :sort WHEN 'Newest' THEN created END DESC," +
" CASE :sort WHEN 'Oldest' THEN created END ASC")
fun all(sort: String): Flow<List<Entity>>
}
Meu modelo de visualização se parece com isto:
class EntityViewModel(application: Application) : AndroidViewModel(application) {
private val entityDao = AppDatabase.getDatabase(context = application.baseContext).entityDao()
var entities: Flow<List<Entity>> = entityDao.all()
private val _selectedSortMode = MutableStateFlow(EntitySortMode.NEWEST)
val selectedSortMode = _selectedSortMode.asStateFlow()
private fun updateDatabaseQuery() {
val sort = _selectedSortMode.value.string
entities = entityDao.all(sort)
}
fun changeSorting(newSortMode: EntitySortMode) {
_selectedSortMode.value = newSortMode
updateDatabaseQuery()
}
}
E meu composable é o seguinte:
@Composable
fun EntityView(viewModel: EntityViewModel) {
val entities by viewModel.entities.collectAsStateWithLifecycle(initialValue = emptyList())
LazyColumn {
items(
entities,
key = {
it.primaryKey
}
) { entity ->
Box(
modifier = Modifier
.animateItem()
.clickable {}
) {
RowItem(entity)
}
}
}
}
Simplifiquei o código para as partes relevantes. Recuperar e exibir dados do banco de dados funciona corretamente, o problema é que alterar a ordem de classificação (chamando changeSorting
) não resulta na recomposição do LazyColumn. Os itens na lista são os mesmos, mas a ordem é diferente, e isso parece impedir que a recomposição ocorra.
O problema é que você declarou o
entities
fluxo comovar
. O Compose observa o fluxo para alterações, mas não pode observar a variável para alterações. Quando você alterna o fluxo, seu composable ainda observará o fluxo antigo. Nenhuma recomposição será acionada. Somente quando o composable for recomposto por algum outro motivo, o novo fluxo será usado. Você não pode controlar isso de forma confiável, então a única maneira de seguir em frente é declararentities
comoval
e removerupdateDatabaseQuery()
.Em vez disso, você precisa alterar de alguma forma o fluxo existente . Existem basicamente duas opções. Além disso, como você deve expor apenas StateFlows em seus modelos de visualização, o código a seguir também mudará isso. Ele cuidará de iniciar e parar adequadamente os fluxos conforme necessário. Além disso, você não precisará mais do valor do fluxo inicial em seus composables.
Agora, as duas opções são:
Baseie o
entityDao.all()
fluxo no_selectedSortMode
fluxo:flatMapLatest
troca um fluxo por outro. Neste caso, o conteúdo do_selectedSortMode
fluxo é usado para recuperar outro fluxo, contendo as entradas do banco de dados classificadas. Sempre que o valor de _selectedSortMode é alterado, o bloco flatMapLatest é executado novamente. O fluxo resultante é então retornado (e transformado em um StateFlow usandostateIn
).Observe que
entities
agora deve ser declarado depois_selectedSortMode
porque o primeiro acessa o último.Faça a classificação em Kotlin, não em SQL:
Seu DAO se torna muito mais simples:
Para o modelo de visualização, você pode usar o código da primeira opção, exceto que agora você precisa substituir a
entities
definição por isto:Os dois fluxos agora são combinados em vez de baseados um em cima do outro. Quando qualquer um dos fluxos fornece um novo valor, o bloco combine inteiro (ou seja, a instrução when) é executado novamente.
Eu preferiria a segunda opção porque é mais simples e flexível caso você decida adicionar opções de classificação adicionais. Fazer a classificação no banco de dados é mais rápido, no entanto. Isso só será perceptível quando sua lista ficar grande. Você precisa pesar o ganho de desempenho em relação ao código mais limpo para descobrir qual opção se encaixa melhor.