Hey! I'm witnessing lot of ui jank issues on compo...
# compose
s
Hey! I'm witnessing lot of ui jank issues on compose lazy column, There is no task being done on the ui layer, have added key parameter a swell. target device works fine in case of recycler view. I was experimenting with constraint layout for the list items in compose is it because of that? have added the code ``````
Copy code
val crimson_500 = Color(0xFFE51A4D)
val crimson_300 = Color(0xFFA40D50)
val text_700 = Color(0xE6FFFFFF)
val text_500 = Color(0xCCFFFFFF)
val dove = Color(0xFF181B25)
@Composable
private fun DurationIcon(modifier: Modifier) {
    return Icon(
        modifier = modifier,
        painter = rememberAsyncImagePainter(R.drawable.ic_clock_16),
        tint = text_500,
        contentDescription = "Duration of show"
    )
}

@Composable
fun BulkDownloadListCard(
    modifier: Modifier = Modifier,
    episodeTitleText: String,
    episodeDurationText: () -> String,
    episodeFileSizeText: () -> String,
    isChecked: Boolean = false,
    isCheckedEnabled: Boolean = true,
    groupFileSizeVisibility: Boolean = true,
    onCardOrCheckClicked: () -> Unit = {},
    bulkDownloadListItemStatus: Int,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    imageUrl: String,
) {
    val context = LocalContext.current
    ConstraintLayout(modifier = modifier
        .background(color = dove)
        .graphicsLayer { alpha = if (isCheckedEnabled) 1.0f else 0.3f }
        .clickable(
            interactionSource = interactionSource,
            indication = rememberRipple(bounded = true),
            onClick = {
                when (bulkDownloadListItemStatus) {
                    DownloadEntity.STATUS_FINISHED -> showToast(
                        context.getString(R.string.this_episode_is_already_downloaded),
                        context = context
                    )

                    DownloadEntity.STATUS_QUEUED,
                    DownloadEntity.STATUS_RUNNING -> showToast(
                        context.getString(R.string.this_episode_is_downloading),
                        context = context
                    )
                    else -> {
                        onCardOrCheckClicked()
                    }
                }
            },
        )
    ) {
        val (checkBox, episodeImage, episodeTitle, episodeDuration, firstDot, fileSize) = createRefs()

        CheckBoxComponent(
            modifier = Modifier
                .scale(.88f)
                .constrainAs(checkBox) {
                    width = Dimension.wrapContent
                    height = Dimension.wrapContent
                    top.linkTo(<http://episodeImage.top|episodeImage.top>)
                    bottom.linkTo(episodeImage.bottom)
                    start.linkTo(parent.start, margin = 7.dp)
                },
            isChecked = isChecked,
            isCheckedEnabled = isCheckedEnabled,
            onCheckedChanged = onCardOrCheckClicked,
        )

        AsyncImage(
            model = ImageRequest.Builder(LocalContext.current)
                .data(imageUrl)
                .crossfade(true)
                .networkCachePolicy(CachePolicy.ENABLED)
                .diskCachePolicy(CachePolicy.ENABLED)
                .build(),
            contentDescription = null,
            modifier = Modifier
                .size(56.dp)
                .constrainAs(episodeImage) {
                    top.linkTo(<http://parent.top|parent.top>, margin = 6.dp)
                    start.linkTo(checkBox.end, margin = 2.dp)
                    bottom.linkTo(parent.bottom, margin = 6.dp)
                }
        )

        Text(
            modifier = Modifier
                .constrainAs(episodeTitle) {
                    width = Dimension.fillToConstraints
                    height = Dimension.wrapContent
                    top.linkTo(<http://episodeImage.top|episodeImage.top>)
                    start.linkTo(episodeImage.end, margin = 16.dp)
                    end.linkTo(parent.end, margin = 50.dp)
                },
            text = episodeTitleText,
            style = PFMTheme.typography.b2.copy(
                fontSize = 13.sp,
                fontWeight = FontWeight.Bold
            ),
            color = text_700,
            overflow = TextOverflow.Ellipsis,
            maxLines = 2,
        )
        Row(
            modifier = Modifier
                .height(IntrinsicSize.Min)
                .constrainAs(episodeDuration) {
                    width = Dimension.wrapContent
                    start.linkTo(episodeTitle.start)
                    top.linkTo(episodeTitle.bottom, margin = 8.dp)
                },
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.Start
        ) {
            DurationIcon(modifier = Modifier.size(22.dp).padding(end = 4.dp))
            Text(
                modifier = Modifier,
                text = episodeDurationText.invoke(),
                style = PFMTheme.typography.b3,
                color = text_500
            )
        }

        if (groupFileSizeVisibility) {
            Text(
                modifier = Modifier
                    .constrainAs(firstDot) {
                        width = Dimension.wrapContent
                        height = Dimension.wrapContent
                        start.linkTo(episodeDuration.end, margin = 4.dp)
                        top.linkTo(<http://episodeDuration.top|episodeDuration.top>)
                        bottom.linkTo(episodeDuration.bottom)

                    },
                text = stringResource(id = R.string.bullet_point),
                style = PFMTheme.typography.b1,
                color = crimson_300
            )
            Row(
                modifier = Modifier
                    .height(IntrinsicSize.Min)
                    .wrapContentSize()
                    .constrainAs(fileSize) {
                        start.linkTo(firstDot.end, margin = 4.dp)
                        top.linkTo(episodeTitle.bottom, margin = 2.dp)
                    },
                verticalAlignment = Alignment.CenterVertically
            ) {
                Icon(
                    modifier = Modifier
                        .size(22.dp)
                        .padding(all = 4.dp),
                    painter = rememberAsyncImagePainter(R.drawable.file_grey_light),
                    contentDescription = "File Size",
                    tint = text_500
                )

                Text(
                    modifier = Modifier,
                    text = episodeFileSizeText.invoke(),
                    style = PFMTheme.typography.b3,
                    color = text_500
                )
            }
        }
    }
}


@Composable
private fun CheckBoxComponent(
    modifier: Modifier,
    isCheckedEnabled: Boolean,
    isChecked: Boolean,
    onCheckedChanged: () -> Unit
) {
    Checkbox(
        modifier = modifier,
        checked = isChecked,
        enabled = isCheckedEnabled,
        onCheckedChange = { onCheckedChanged() },
        colors = CheckboxDefaults.colors(
            checkedColor = crimson_500,
            checkmarkColor = dove,
            uncheckedColor = Color.White,
            disabledCheckedColor = Color.Black,
            disabledUncheckedColor = Color.Black,
            disabledIndeterminateColor = Color.Black
        )
    )
}
s
ConstraintLayout uses LateMotionLayout LateMotionLayout uses MultiMeasureLayout And MultiMeasureLayout looks to be deprecated with the message: "This API is unsafe for UI performance at scale - using it incorrectly will lead to exponential performance issues. This API should be avoided whenever possible." Combine this with a LazyLayout I simply would not trust it myself. If you remove the constraint layout and instead use something which does not measure itself many times, and optimally does not even use SubComposition itself do you notice the same lags?
s
Got it, will remove constraint layout thanks for the help @Stylianos Gakis .
s
To make this easy for yourself, just remove the ConstraintLayout, replace it with a Box() which just places all items on top of each other, no matter how ugly that looks like, and just run the app. Then try to notice if you get the same laggy behavior. If yes, it's not the ConstraintLayout