Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

600
Vistas
Jetpack Compose – LazyColumn no se recompone

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

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.

Editar.

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 }
over 4 years ago · Hanz Gallego
2 Respuestas
Responde la pregunta

0

Los cachorros de Flow está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 }
over 4 years ago · Hanz Gallego Denunciar

0

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:

  1. Reasignar la lista en lugar de modificarla en el lugar (por ejemplo, hacer que los selectables MutableList y realizar selectables[index] = tappedItem.copy(selected = !tappedItem.selected) no funcionaría)
  2. Reasignar el elemento seleccionado en lugar de modificarlo en el lugar, por ejemplo, lo siguiente 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.

over 4 years ago · Hanz Gallego Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda