I'm new on jetpack compose and I'm sure that I'm missing something but I don't know what?
my State model:
data class ChoiceSkillsState(
val isLoading: Boolean = false,
val errorWD: ErrorWD? = null,
val skills: List<Skill> = emptyList(),
)
The Skill model:
@Parcelize
data class Skill(
val id: Int,
val name: String,
val imageUrl: String? = null,
var children: List<SkillChild>? = null,
) : Parcelable {
@Parcelize
data class SkillChild(
val id: Int,
val name: String,
val imageUrl: String? = null,
var note: Int? = null,
) : Parcelable
}
fun Skill.asChildNoted(): Boolean {
if (!children.isNullOrEmpty()) {
children!!.forEach {
if (it.note != null) return true
}
}
return false
}
on my viewModel
private val _state = mutableStateOf(ChoiceSkillsState())
val state: State<ChoiceSkillsState> = _state
On some event I update my skillList on my state : ChoiceSkillState. When I log, my data is updated correctly but my view is not recomposed..
There is my LazyColumn:
@Composable
private fun LazyColumnSkills(
skills: List<Skill>,
onClickSkill: (skill: Skill) -> Unit,
) {
LazyColumn(
contentPadding = PaddingValues(bottom = MaterialTheme.spacing.medium),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.small),
) {
items(
items = skills,
) { skill ->
ItemSkillParent(
skill = skill,
onClickSkill = onClickSkill
)
}
}
}
Then here is my ItemSkillParent:
@Composable
fun ItemSkillParent(
skill: Skill,
onClickSkill: (skill: Skill) -> Unit
) {
val backgroundColor =
if (skill.asChildNoted()) Orange
else OrangeLight3
val endIconRes =
if (skill.asChildNoted()) R.drawable.ic_apple
else R.drawable.ic_arrow_right
Box(
modifier = Modifier
.fillMaxWidth()
.clip(shape = MaterialTheme.shapes.itemSkill)
.background(backgroundColor)
.clickable { onClickSkill(skill) },
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 7.dp, horizontal = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
modifier = Modifier
.weight(1f)
.size(50.dp)
.clip(shape = MaterialTheme.shapes.itemSkillImage),
painter = rememberAsyncImagePainter(model = skill.imageUrl),
contentDescription = "Image skill",
contentScale = ContentScale.Crop
)
Text(
modifier = Modifier
.weight(6f)
.padding(horizontal = 10.dp),
text = skill.name,
style = MaterialTheme.typography.itemSkill
)
ButtonIconRoundedMini(
iconRes = endIconRes,
contentDesc = "Icon arrow right",
onClick = { onClickSkill(skill) }
)
}
}
}
My onClickSkill() will open a new Screen then pass result, then I will update my data with this :
fun updateSkill(skill: Skill) {
val skillsUpdated = _state.value.skills
skillsUpdated
.filter { it.id == skill.id }
.forEach { it.children = skill.children }
_state.value = _state.value.copy(skills = skillsUpdated)
}
As you can see, the background color and the iconResource should be changed, it's changing when only when I scroll. Can someone explain me what's happening there ?
CodePudding user response:
make your properties as state
val backgroundColor by remember {
mutableStateOf (if (skill.asChildNoted()) Orange
else OrangeLight3)
}
val endIconRes by remember {
mutableStateOf (if (skill.asChildNoted()) R.drawable.ic_apple else R.drawable.ic_arrow_right)
}
CodePudding user response:
You should never use var
in class properties if you want a property update to cause recomposition.
Check out Why is immutability important in functional programming?.
In this case you are updating the children
property, but skillsUpdated
and _state.value.skills
are actually the same object - you can check the address, so Compose thinks it has not been changed.
After updating your children
to val
, you can use copy
to update it.
val skillsUpdated = _state.value.skills.toMutableList()
for (i in skillsUpdated.indices) {
if (skillsUpdated[i].id != skill.id) continue
skillsUpdated[i] = skillsUpdated[i].copy(children = skill.children)
}
_state.value = _state.value.copy(skills = skillsUpdated.toImmutableList())
Note that converting a mutable list into an immutable list is also critical here, because otherwise the next time you try to update it, the list will be the same object: both toList
and toMutableList
return this
when applied to a mutable list.