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

Deepak Gahlot

02/24/2021, 2:01 PM
🧵 I have one query related to "transitionState" for animations in jetpack compose. I have created an expandable list which on the icon click expands and collapse, which certain animations. What i'm seeing is that the Composable function which shows expanded content, is being executed 4 times. And that is the same number of transition i'm applying.
Is this something expected ?
Copy code
@ExperimentalFoundationApi
@ExperimentalAnimationApi
@Composable
fun ExpandableCard(
    card: ItemsItem,
    questions: List<ItemsItem>,
    qResponse: Response
) {
    val expanded = remember { mutableStateOf(false) }

    val transitionState = remember {
        MutableTransitionState(expanded.value).apply {
            targetState = !expanded.value
        }
    }
    val transition = updateTransition(transitionState)
    val cardPaddingHorizontal by transition.animateDp({
        tween(durationMillis = EXPAND_ANIMATION_DURATION)
    }) {
        if (expanded.value) 10.dp else 10.dp
    }
    val cardElevation by transition.animateDp({
        tween(durationMillis = EXPAND_ANIMATION_DURATION)
    }) {
        if (expanded.value) 10.dp else 4.dp
    }
    val cardRoundedCorners by transition.animateDp({
        tween(
            durationMillis = EXPAND_ANIMATION_DURATION,
            easing = FastOutSlowInEasing
        )
    }) {
        if (expanded.value) 0.dp else 5.dp
    }
    val arrowRotationDegree by transition.animateFloat({
        tween(durationMillis = EXPAND_ANIMATION_DURATION)
    }) {
        if (expanded.value) 0f else 180f
    }
    Card(
        contentColor = colorResource(id = R.color.white),
        elevation = cardElevation,
        shape = RoundedCornerShape(cardRoundedCorners),
        modifier = Modifier
            .fillMaxWidth()
            .padding(
                horizontal = cardPaddingHorizontal,
                vertical = 8.dp
            )
    ) {
        Column {
            Row(
                modifier = Modifier
                    .background(Color.White)
                    .padding(20.dp, 10.dp, 0.dp, 10.dp)
                    .fillMaxWidth()
                    .animateContentSize(),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                @Suppress("DEPRECATION")
                ConstraintLayout(
                    modifier = Modifier.fillMaxWidth()
                ) {
                    val (textField, iconImage) = createRefs()
                    Text(
                        text = card.qualifiedNumber + " " + card.label,
                        modifier = Modifier.constrainAs(textField) {
                        }
                            .padding(0.dp, 10.dp, 0.dp, 0.dp)
                            .fillMaxWidth(),
                        color = colorResource(id = R.color.black)
                    )
                    IconButton(
                        onClick = { if(expanded.value) { expanded.value = false } else { expanded.value = true } },
                        modifier = Modifier.constrainAs(iconImage) {
                            end.linkTo(parent.absoluteRight)
                        },
                        content = {
                            Icon(
                                painter = painterResource(id = R.drawable.ico_expand_arrow),
                                contentDescription = "Expandable Arrow",
                                modifier = Modifier.rotate(arrowRotationDegree),
                                tint = Color(R.color.black)
                            )
                        }
                    )
                }
            }
            //Add the expandable content here
            ExpandableContent(
                questions as ArrayList<ItemsItem>,
                qResponse, visible = expanded.value,
                initialVisibility = expanded.value
            )
        }
    }

}
this is the Code block i have written and the Card composable which is the parent is being executed for times, which i tap on the Icon for expanding the Layout. It is working as expected , the logic for expand - collapse is working flawless, but since it is calling the child composable number of times, i'm processing a huge list of data that is also being called multiple times which is causing performance issues on the UI (edited)
⁉️ 1
d

Doris Liu

02/25/2021, 6:54 AM
Sorry for the late reply. I played around with the code snippet and was able to reproduce the additional recompositions. It's not immediately clear to me what's causing the recompositions. Could you file a bug here: https://issuetracker.google.com/issues?q=componentid:612128 with the code snippet? I'll investigate further. 🙂
Actually, after taking a further look, in the
Transition.animateFloat/animateDp
call, the
if (expanded.value) 0f else 180f
is causing additional composition. In that lambda it's recommended to use the targetState that is passed to the lambda:
if (it) 0f else 100f
so that external state change doesn't directly cause animations to recompose.
d

Deepak Gahlot

02/25/2021, 7:14 AM
so should we do that for all the other animations as well.
d

Doris Liu

02/25/2021, 7:15 AM
Yes. Please let me know if you still have issues after changing that. We should probably add a lint for this. 🤔
d

Deepak Gahlot

02/25/2021, 7:15 AM
what i understood from your comment, is that we need to use targetState instead of using expanded.value
Sure i will try and let you know, thanks for the quick help. 🙏
d

Doris Liu

02/25/2021, 7:18 AM
Not the targetState of
Transition
, but targetState that is passed to the lambda. I.e. I'd change this
Copy code
{
        if (expanded.value) 0f else 180f
    }
to
Copy code
{it ->
        if (it) 0f else 180f
    }
This is because when we support seeking, we may pass different
targetState
than the actual one to query both the start and end values for seeking purposes. 🙂
d

Deepak Gahlot

02/25/2021, 7:19 AM
got it, make sense.
👍 1
d

Doris Liu

02/25/2021, 7:32 AM
Created an issue to add lint as a follow-up in case you are interested: https://issuetracker.google.com/181195383
simple smile 1
👀 1
BTW, if you have android studio canary8, I'd encourage you to check the animations in the animation tools (as the tooling uses seeking under the hood). This is what the animation tools look like: https://developer.android.com/jetpack/compose/tooling#animations
d

Deepak Gahlot

02/25/2021, 7:48 AM
This is cool. I was in the process of updating the project to beta:01. Sure will try this out thanks again
👍 1