Reputation: 1215
Say that, I'm building a custom compose layout and populating that list as below
val list = remember { dataList.toMutableStateList()}
MyCustomLayout{
list.forEach { item ->
key(item){
listItemCompose( data = item,
onChange = { index1,index2 -> Collections.swap(list, index1,index2)})
}
}
This code is working fine and the screen gets recomposed whenever onChange lambda function is called, but when it comes to any small change in any item's property, it does not recompose, to elaborate that let's change the above lambda functions to do the following
{index1,index2 -> list[index1].propertyName = true}
Having that lambda changing list item's property won't trigger the screen to recompose. I don't know whether this is a bug in jetpack compose or I'm just following the wrong approach to tackle this issue and I would like to know the right way to do it from Android Developers Team. That's what makes me ask if there is a way to force-recomposing the whole screen.
Upvotes: 35
Views: 41273
Reputation: 162
You can use:
currentCompositionScope.invalidate()
But use with care.
compose version: 1.7.5
Upvotes: 1
Reputation: 774
You can force recompose using key:
val text by remember { mutableStateOf("foo") }
key(text) {
YourComposableFun(
onClick = {
text = "bar"
}
) {
}
}
In this example the text is keyed. When its value changes in the onClick, everything inside the key function recomposes.
Upvotes: 48
Reputation: 11
For enabling compose to track changes to your list's element data type properties, you could use mutableStateOf(initialValue)
function to define your property in the data type definition.
as an example:
class Task(
val id: Int,
val label: String,
initialChecked: Boolean = false
) {
var checked by mutableStateOf(initialChecked)
}
now, compose will trigger recomposition whenever checked
proeprty is mutated.
Upvotes: 0
Reputation: 4395
If you ever need to force a recompose then create an empty LaunchedEffect
with a MutableState<Boolean>
value key and toggle the mutable state.
class FooViewHolder {
private val recomposeToggleState: MutableState<Boolean> = mutableStateOf(false)
@Composable
fun View() {
Log.i("Foo View Holder", "Compose / Recompose")
Box(modifier = Modifier
.fillMaxSize()
.background(Color.Green)
)
LaunchedEffect(recomposeToggleState.value) {}
}
fun manualRecompose() {
recomposeToggleState.value = !recomposeToggleState.value
}
}
Then in Activity
for example
val viewHolder = FooViewHolder()
override fun onCreate(savedInstanceState: Bundle?) {
setContent {
viewHolder.View()
}
}
override fun onResume() {
viewHolder.manualRecompose()
}
Upvotes: 5
Reputation: 66929
With SnapshotStateList to trigger recomposition you need delete, insert or update existing item with a new item with the property you set as using a data class is best option since it comes out with a copy function
list[index1] = list[index1].copy(propertyName = true)
will trigger recomposition since you set a new item at index1 with property = true
https://stackoverflow.com/a/74506067/5457853
https://stackoverflow.com/a/74700668/5457853
Upvotes: 1
Reputation: 29280
You can't force a composable function to recompose, this is all handled by the compose framework itself, there are optimizations to determine when something has changed that would invalidate the composable and to trigger a recomposition, of only those elements that are affected by the change.
The problem with your approach is that you are not using immutable classes to represent your state. If your state changes, instead of mutating some deep variable in your state class you should create a new instance of your state class (using Kotin's data class
), that way (by virtue of using the equals
in the class that gets autogenerated) the composable will be notified of a state change and trigger a recomposition.
Compose works best when you use UDF (Unidirectional Data Flow) and immutable classes to represent the state.
This is no different than, say, using a LiveData<List<Foo>>
from the view system and mutating the Foo
s in the list, the observable for this LiveData
would not be notified, you would have to assign a new list to the LiveData
object. The same principle applies to compose state.
Upvotes: 20