Home > database >  Animate visibility in compose
Animate visibility in compose

Time:11-18

I have a text which need to be animated to show and hide with the value is null or not. it would have been straight forward if the visibility is separately handle, but this is what I got. In the bellow code the enter animation works but the exit animation dont as the text value is null. I can think of something with remembering the old value but not sure how.

@Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = text != null,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            text?.let {
                Text(text = it)
            }
        }
    }
}

CodePudding user response:

I think the fade-out animation is actually working "per-se".

I suspect the parameter text: String? is a value coming from a hoisted "state" somewhere up above ShowAnimatedText, and since you are directly observing it inside the animating scope, when you change it to null it instantly removes the Text composable, and your'e not witnessing a slow fade out.

AnimatedVisibility(
    ...
) {
    text?.let { // your'e directly observing a state over here
        Text(text = it)
    }
}

This is my attempt completing your snippet based on my assumption and making it work, the fade-in works, but the desired fade-out is instantly happening.

@Composable
fun SomeScreen() {

    var text by remember {
        mutableStateOf<String?>("Initial Value")
    }

    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Button(onClick = {
            text = "New Value"
        }) {
            Text("Set New Value")
        }

        Button(onClick = {
            text = null
        }) {
            Text("Remove Value")
        }

        AnimatedText(text = text)
    }
}

@Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = text != null,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            text?.let {
                Text(text = it)
            }
        }
    }
}

You can solve it by modifying the text to a non-state value and change your visibility logic from using a nullability check to some "business logic" that would require it to be visible or hidden, modifying the codes above like this.

@Composable
fun SomeScreen() {

    var show by remember {
        mutableStateOf(true)
    }

    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Button(onClick = {
            show = !show
        }) {
            Text("Set New Value")
        }

        AnimatedText(text = "Just A Value", show)
    }
}

@Composable
fun ShowAnimatedText(
    text : String?,
    show: Boolean
) {

    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = show,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {

            text?.let {
                Text(text = it)
            }
        }
    }
}

enter image description here

CodePudding user response:

I fixed it by remembering the previous state (Or don't set the null value) until the exit animation is finished, if the text is null.

Thank you z.y for your suggestion.

@Composable
fun ShowAnimatedText(
    text : String?,
    show: Boolean
) {
    var localText by remember {
        mutableStateOf<String?>(null)
    }
    AnimatedContent(show, localText)
    LaunchedEffect(key1 = text, block = {
        if(text == null){
            delay(2000)
        }
        localText = text
    })
}
@Composable
private fun AnimatedContent(show: Boolean, localText: String?) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = show,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            localText?.let {
                Text(text = it)
            }
        }
    }
}
  • Related