In jetpack compose,
We can convert MutableStateFlow<T>
to State<T>
using collectAsState()
and with delegation by
can directly assign T
to the variable.
Now as I understand, it doesn't matter much if I used MutableState<T>
or MutableStateFlow<T>
to hold the state, performance-wise.
But for List types however, it seems there is no special version of Flow, ie, if you really need, you can Flow<List<T>
, which I've seen used most of the time. Then, if you apply collectAsState()
on Flow<List<T>
, you get State<List<T>
.
Now, if I understand correctly, even if a single value was changed (added, removed, modified) in the list the whole list state would be considered new thus all respective list item UI components would be recomposed. While with SnapshotStateList<T>
, only the respective element UI will be (re)composed. Or, Jetpack compose has some internal optimization for State<List<T>
that prevents these types of useless recomposition and I should not worry about it.
According to my tests, LazyColumn will NOT recompose items as long as you provide the key, even if the it's from State<List<T>
.
My test sample:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestSampleTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val vm: SimpleViewModel = viewModel()
val list by vm.list.collectAsState()
SimplePage(
list = list,
onClick = {
vm.add()
vm.shuffle()
},
)
}
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SimplePage(
list: List<Int>,
onClick: () -> Unit,
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
LazyColumn(
) {
items(
list,
key = { it }
) {
Text(
modifier = Modifier.animateItemPlacement(),
text = it.toString()
)
}
}
Button(onClick = onClick) {
}
}
}
class SimpleViewModel : ViewModel() {
// val list = MutableStateFlow(listOf(1, 5, 8, 3, 7, 6))
val list = MutableStateFlow(listOf(0))
fun shuffle() = list.update {
it.toMutableList().apply {
shuffle()
}
}
fun add() = list.update {
it.toMutableList().apply {
add(max() + 1)
}
}
}
Here, Modifier.animateItemPlacement()
only works when you ensure unique key
other than positional. And key
itself ensures no recomposition for the same item. https://stackoverflow.com/a/70596559/13519865
https://stackoverflow.com/a/68794279/13519865
Finally, animateItemPlacement
works, thus key used nicely.
So, we can safely assume that we can use State<List<T>
for it's Flow operation benefits without thinking much about recomposition cost.