Mi LazyColumn no se está recomponiendo pero el valor se está actualizando.
Si me desplazo hacia abajo en la lista y retrocedo hacia arriba, veo los valores correctos para la interfaz de usuario
Actividad principal
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyTheme { MyApp() } } } } // Start building your app here! @Composable fun MyApp(vm: PuppyListViewModel = viewModel()) { val puppers by vm.pups.collectAsState(emptyList()) Surface(color = MaterialTheme.colors.background) { Column { Toolbar() LazyColumn { items(puppers) { pup -> PuppyUI(pup, vm::seeDetails, vm::togglePuppyAdoption) } } } } }El modelo de vista
class PuppyListViewModel : ViewModel() { val pups = PuppyRepo.getPuppies().onEach { println("FlowEmitted: $it") } fun togglePuppyAdoption(puppy: Puppy) = viewModelScope.launch { PuppyRepo.toggleAdoption(puppy.id) } fun seeDetails(puppy: Puppy) { println("seeDetails $puppy") } }El modelo
internal var IDS = 0L data class Puppy ( val name: String, val tagline: String = "", val race: String, @DrawableRes val image: Int, var adopted: Boolean = false, val id: Long = ++IDS, )el repositorio
object PuppyRepo { private val changeFlow = MutableStateFlow(0) private val pups: List<Puppy> private val puppyImages = listOf( R.drawable._1, R.drawable._2, R.drawable._3, R.drawable._4, R.drawable._5, R.drawable._6, R.drawable._7, R.drawable._8, R.drawable._9, R.drawable._10, R.drawable._11, R.drawable._12, R.drawable._13, R.drawable._14, R.drawable._15, R.drawable._16, R.drawable._17, R.drawable._18, R.drawable._19, ) private val puppyNames = listOf( "Gordie", "Alice", "Belle", "Olivia", "Bubba", "Pandora", "Bailey", "Nala", "Rosco", "Butch", "Matilda", "Molly", "Piper", "Kelsey", "Rufus", "Duke", "Ozzy" ) private val puppyTags = listOf( "doggo", "doge", "special dogo", "wrinkler", "corgo", "shoob", "puggo", "pupper", "small dogo", "big ol dogo", "woofer", "floofer", "yapper", "pupper", "good-boye", "grizlord", "snip-snap dogo" ) private val puppyBreeds = listOf( "Labrador Retriever", "German Shepard", "Golden Retriever", "French Bulldog", "Bulldog", "Beagle", "Poodle", "Rottweiler", "German Shorthaired Pointer", "Yorkshire Terrier", "Boxer" ) init { pups = puppyImages.map { image -> val name = puppyNames.random() val tagline = puppyTags.random() val breed = puppyBreeds.random() Puppy(name, tagline, breed, image) } } @OptIn(ExperimentalCoroutinesApi::class) fun getPuppies() = changeFlow.flatMapLatest { flowOf(pups) } fun getPuppy(puppyId: Long) = flow { emit(pups.find { it.id == puppyId }) } suspend fun toggleAdoption(puppyId: Long): Boolean { val found = getPuppy(puppyId).first()?.toggleAdoption()?.let { true } ?: false if (found) { // Trigger a new emission for those that are consuming a Flow from getPuppies changeFlow.value = changeFlow.value + 1 } return found } private fun Puppy.toggleAdoption() { adopted = !adopted } } Los cachorros de Flow están produciendo valores actualizados como se puede ver en mi logcat
He puesto declaraciones de impresión en mis componibles y no se vuelven a componer después de que el flujo emite un nuevo valor.
Parece que Compose compara las referencias de los objetos y, dado que no cambiaron, la recomposición no ocurrió incluso si los flujos emitían nuevos valores (¿un error en Compose quizás?)
Cambió la funcionalidad de toggle para volver a crear las instancias de los elementos de la lista como la siguiente y ahora está funcionando.
Nota: he hecho que Puppy.adopted un val en lugar de var
suspend fun toggleAdoption(puppyId: Long): Boolean { var found = false pups = pups.map { val isThePuppy = it.id == puppyId found = found || isThePuppy if(isThePuppy) it.copy(adopted = !it.adopted) else it.copy() } if (found) { // Trigger a new emission for those that are consuming a Flow from getPuppies changeFlow.value = changeFlow.value + 1 } return found }Los cachorros de
Flowestán produciendo valores actualizados como se puede ver en mi logcat
No exactamente.
El Flow está emitiendo la misma List de los mismos objetos Puppy . Creo que Compose ve que List es el mismo objeto List que antes y asume que no hay cambios.
Mis cambios sugeridos:
Hacer que Puppy sea una clase data inmutable (es decir, sin propiedades var )
Deshazte de changeFlow y haz que getPuppies() devuelva un MutableStateFlow<List<Puppy>> estable (o haz que solo sea una propiedad pública)
En toggleAdoption() , crea una nueva lista de objetos Puppy y utilízala para actualizar MutableStateFlow<List<Puppy>> :
suspend fun toggleAdoption(puppyId: Long) { val current = puppies.value // assumes that puppies is a MutableSharedFlow<List<Puppy>> val replacement = current.map { if (it.id == puppyId) it.copy(adopted = !it.adopted) else it } puppies.value = replacement }Esto funcionó para mí.
import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel class MyViewModel : ViewModel() { var selectables: List<Selectable> by mutableStateOf(List(100) { Selectable(name = "$it") }) private set fun onTapped(tappedItem: Selectable) { val index = selectables.indexOf(tappedItem) selectables = selectables.toMutableList().also { it[index] = tappedItem.copy(selected = !tappedItem.selected) } } } data class Selectable( val name: String, var selected: Boolean = false, )Las partes clave son:
selectables MutableList y realizar selectables[index] = tappedItem.copy(selected = !tappedItem.selected) no funcionaría) selectables = selectables.toMutableList().also { it[index].selected = !tappedItem.selected }Tenga en cuenta que no tiene que hacer que su clase de datos sea inmutable; sin embargo, hacerla inmutable obligará a hacer una copia del elemento para actualizarlo.