Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose - Cannot animate Scaffold's bottom bar's height

I need to reveal some floating action buttons at the bottom of the screen when some other buttons, in a scrollable list, are no longer visible on the screen. Naturally, I feel like using LazyColumn, AnimatedVisibility and Scaffold's bottom bar for this (I need the snackbar and the screen's content be above the floating action buttons). But the animation doesn't work as expected. As the floating buttons appear/dissapear, the bottom bar's height is not animated (but I think it could actually be that the content padding isn't animated).

The result I'm getting:

enter image description here

The code:

val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by derivedStateOf {
    lazyListState.firstVisibleItemIndex >= 2
}
Scaffold(
    bottomBar = {
        AnimatedVisibility(
            visible = isFloatingButtonVisible,
            enter = slideInVertically { height -> height },
            exit = slideOutVertically { height -> height }
        ) {
            Row(
                modifier = Modifier.padding(8.dp),
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                repeat(2) {
                    Button(
                        modifier = Modifier
                            .height(48.dp)
                            .weight(1f),
                        onClick = {}
                    ) {
                        Text(text = "Something")
                    }
                }
            }
        }
    }
) { contentPadding ->
    LazyColumn(
        modifier = Modifier.padding(contentPadding),
        state = lazyListState,
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        item {
            Text(
                text = LoremIpsum(50).values.joinToString(" ")
            )
        }
        item {
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                repeat(2) {
                    Button(onClick = {}) {
                        Text(text = "Bla Bla")
                    }
                }
            }
        }
        item {
            Text(
                text = LoremIpsum(700).values.joinToString(" ")
            )
        }
    }
}
like image 840
daniyelp Avatar asked Oct 12 '25 07:10

daniyelp


2 Answers

Got the right result using AnimatedContent instead of AnimatedVisibility.

enter image description here

val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by derivedStateOf {
    lazyListState.firstVisibleItemIndex >= 2
}
Scaffold(
    bottomBar = {
        AnimatedContent(
            targetState = isFloatingButtonVisible,
            transitionSpec = {
                slideInVertically { height -> height } with
                slideOutVertically { height -> height }
            }
        ) { isVisible ->
            if (isVisible) {
                Row(
                    modifier = Modifier
                        .border(1.dp, Color.Red)
                        .padding(8.dp),
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    repeat(2) {
                        Button(
                            modifier = Modifier
                                .height(48.dp)
                                .weight(1f),
                            onClick = {}
                        ) {
                            Text(text = "Something")
                        }
                    }
                }
            } else {
                Box(modifier = Modifier.fillMaxWidth())
            }
        }
    }
) { contentPadding ->
    LazyColumn(
        modifier = Modifier.padding(contentPadding),
        state = lazyListState,
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        item {
            Text(
                text = LoremIpsum(50).values.joinToString(" ")
            )
        }
        item {
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                repeat(2) {
                    Button(onClick = {}) {
                        Text(text = "Bla Bla")
                    }
                }
            }
        }
        item {
            Text(
                text = LoremIpsum(700).values.joinToString(" ")
            )
        }
    }
}
like image 133
daniyelp Avatar answered Oct 14 '25 19:10

daniyelp


I don't think it's possible to do this with AnimatedVisibility. I got it like this

@Composable
fun Test() {
    val lazyListState = rememberLazyListState()

    val isFloatingButtonVisible by remember {
        derivedStateOf {
            lazyListState.firstVisibleItemIndex >= 1
        }
    }

    var barHeight by remember { mutableStateOf(0.dp) }


    var listSizeDelta by remember { mutableStateOf(0.dp) }
    val bottomBarOffset = remember { Animatable(barHeight, Dp.VectorConverter) }

    LaunchedEffect(isFloatingButtonVisible) {
        if (isFloatingButtonVisible) {
            bottomBarOffset.animateTo(barHeight)
            listSizeDelta = barHeight
        } else {
            listSizeDelta = 0.dp
            bottomBarOffset.animateTo(0.dp)
        }
    }

    val density = LocalDensity.current

    BoxWithConstraints(Modifier.fillMaxSize()) {
        LazyColumn(
            modifier = Modifier
                .size(width = maxWidth, height = maxHeight - listSizeDelta)
                .background(Color.Yellow),
            state = lazyListState,
            contentPadding = PaddingValues(16.dp),
            verticalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            repeat(15) {
                item {
                    Text(
                        text = LoremIpsum(50).values.joinToString(" ")
                    )
                }
            }
        }

        Row(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .graphicsLayer {
                    translationY =
                        density.run { (-bottomBarOffset.value + barHeight).toPx() }
                }
                .background(Color.Green)
                .onSizeChanged {
                    barHeight = density.run { it.height.toDp()}
                }
                .padding(8.dp),
            horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            repeat(2) {
                Button(
                    modifier = Modifier
                        .height(48.dp)
                        .weight(1f),
                    onClick = {}
                ) {
                    Text(text = "Something")
                }
            }
        }
    }
}
like image 40
vitidev Avatar answered Oct 14 '25 20:10

vitidev