https://kotlinlang.org logo
#compose
Title
# compose
m

Maciej S

11/30/2022, 11:49 AM
Hey people, have a question about sealed interfaces and stability/skippability 🧵
Given I have a model like
Copy code
sealed interface AvatarUiModel {
    data class ParticipantInitial(val value: String) : AvatarUiModel
    object DraftIcon : AvatarUiModel
}
And a composable that takes this type as a param, like
Copy code
@Composable
fun Avatar(
    modifier: Modifier = Modifier,
    avatarUiModel: AvatarUiModel
) {
...
that is a part of lazy column’s item composable, I can see in the layout inspector’s recomposition counts that the
Avatar
composable is being recomposed all the time while scrolling through the list. This is not the case if I simply use a sealed class, like
Copy code
sealed class AvatarUiModel {
    data class ParticipantInitial(val value: String) : AvatarUiModel()
    object DraftIcon : AvatarUiModel()
}
If I do that, I can see that the
Avatar
recompositions are being skipped while scrolling, as expected. The compose compiler report marks the
Avatar
composable as skippable when using the interface, however, the param is not marked as stable (see the attached screenshot). Is that behaviour expected?
z

Zach Klippenstein (he/him) [MOD]

11/30/2022, 11:31 PM
Good question – @Leland Richardson [G]?
l

Leland Richardson [G]

11/30/2022, 11:47 PM
its skippable because the avataruimodel is runtime stability (versus known stable or known unstable) what does the callsite look like? i am not 100% sure what we do here, I should add a test case for it
m

Maciej S

12/01/2022, 8:26 AM
Thanks for taking a look, here’s how we use the
Avatar
composable:
Copy code
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MailboxItem(
    modifier: Modifier = Modifier,
    item: MailboxItemUiModel,
    onItemClicked: (MailboxItemUiModel) -> Unit,
    onOpenSelectionMode: () -> Unit
) {
    Box(
        modifier = ...
    ) {

        MailboxItemLayout(
            avatar = { Avatar(avatarUiModel = item.avatar) },
            ...
        )
    }
}
and the
MailboxItem
is called from a lazy column:
Copy code
@Composable
@OptIn(ExperimentalFoundationApi::class)
private fun MailboxItemsList(
    listState: LazyListState,
    items: LazyPagingItems<MailboxItemUiModel>,
    actions: MailboxScreen.Actions
) {
    LazyColumn(
        state = listState,
        modifier = ...
    ) {
        items(
            items = items,
            key = { it.id }
        ) { item ->
            item?.let {
                MailboxItem(
                    modifier = Modifier.animateItemPlacement(),
                    item = item,
                    onItemClicked = actions.onNavigateToMailboxItem,
                    onOpenSelectionMode = actions.onOpenSelectionMode
                )
            }
        }
    }
}
l

Leland Richardson [G]

12/01/2022, 6:00 PM
i believe in this case we don't currently know what the stability of the thing returned by
item.avatar
is, so it ends up being "unstable". Theoretically we could do better here because we could infer a sealed interface as being always stable if we know that all of its implementations are always stable. We don't currently do that but it is a good idea
m

Maciej S

12/01/2022, 6:17 PM
Thanks for the explanation! Perhaps it would be worth adding this information to one of those ‘performance tips’ types of articles/videos out there which touch on making the types stable/avoiding recompositions? I imagine we are not the only ones making the assumption that sealed classes and sealed interfaces would be equivalent in terms of stability.
l

Leland Richardson [G]

12/01/2022, 8:23 PM
it's a reasonable question, but not a clear answer. these types of "tips" or "rules" have a habit of living longer than the problem they are helping workaround. I would rather we create a bug for this and just fix it than have it spelled out in a document as a tip somewhere
m

Maciej S

12/05/2022, 8:02 AM
Fair point, sounds good 👍
Hey, coming back to this: do you already have an issue for this on your side that we could track or should I add one?
260 Views