Reputation: 1255
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:
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(" ")
)
}
}
}
Upvotes: 3
Views: 2200
Reputation: 1255
Got the right result using AnimatedContent
instead of AnimatedVisibility
.
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(" ")
)
}
}
}
Upvotes: 5
Reputation: 980
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")
}
}
}
}
}
Upvotes: 1
Reputation: 1473
The content padding in lazycolumn modifier breaks the animation;
LazyColumn(
modifier = Modifier.padding(contentPadding) // -> Delete this.
)
For the same view;
Row(
modifier = Modifier
.background(Color.White) //-> Add this.
.padding(8.dp)
)
Upvotes: -1