Reputation: 334
On using a List<Interface>
with the LazyColumn recomposes all the items, instead if I use List<Implementation>
it skips the recompositions intelligently.
For instance, if I use example 1 MutableList
in composable recompostition happens for all items on addition operation to list, but in example 2 it skips intelligently. WHY?
Example 1:
val selectionState: MutableList<ViewItemScratch> = remember {
mutableStateListOf()
}
Example 2:
val selectionState: MutableList<TitleViewItemScratch> = remember {
mutableStateListOf()
}
Interface:
interface ViewItemScratch {
val identifier: Int
val layoutId: Int
@SuppressLint("NotConstructor")
@Composable
fun ViewItem(
lazyItemScope: LazyItemScope? = null,
rowScope: RowScope? = null,
columnScope: ColumnScope? = null
) {
}
}
Implementation Class:
data class TitleViewItemScratch(
val index: Int,
val text: StringResolver,
val modifier: Modifier = Modifier,
override val identifier: Int = text.hashCode()
) : ViewItemScratch {
override val layoutId: Int
get() = R.id.compose_view_title_view_item
@Composable
override fun ViewItem(
lazyItemScope: LazyItemScope?, rowScope: RowScope?, columnScope: ColumnScope?
) {
Text(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(horizontal = 16.dp),
text = text.resolve(),
)
}
}
Composable:
@Composable
private fun WithViewItemScratch() {
val selectionState: MutableList<ViewItemScratch> = remember {
mutableStateListOf()
}
val onButtonClick = remember {
{
selectionState.add(
TitleViewItemScratch(
index = selectionState.size + 1,
text = UIString("Hello world #${selectionState.size + 1}"),
identifier = selectionState.size + 1,
)
)
}
}
Column {
Button(onClick = {
onButtonClick.invoke()
}) {
Text(text = "Add")
}
LazyColumn {
items(
items = selectionState,
key = { viewItem: ViewItemScratch -> viewItem.identifier }
) { item ->
item.ViewItem()
}
}
}
}
Compiler report(s):
restartable skippable fun ViewItem(
unused stable lazyItemScope: LazyItemScope? = @static null
unused stable rowScope: RowScope? = @static null
unused stable columnScope: ColumnScope? = @static null
unused <this>: ViewItemScratch
)
stable class TitleViewItemScratch {
stable val index: Int
stable val text: StringResolver
stable val color: CompassColor
stable val typography: CompassTypography
stable val modifier: Modifier
stable val identifier: Int
<runtime stability> = Stable
}
Layout Inspector recompositions:
With List of Implementation/Concrete class:
Upvotes: 1
Views: 672
Reputation: 954
Extracting the Jetpack compose compiler report/metrics shows that the interface's stability is missing. Because the interface doesn't have logic but tells about the abstraction it brings. So if the interface doesn't explicitly promise the compose stability by applying @Stable
or @Immutable
, it's treated as unstable only.
The implementation is considered stable by default because it's a data class (which comes with equals()
and hashCode()
implementation) with all properties as val
(immutable).
Upvotes: 3