Skip to content

Instantly share code, notes, and snippets.

@joharei
Last active September 13, 2021 10:50
Show Gist options
  • Save joharei/2a4765f8fc39878a669f29055c1af1bc to your computer and use it in GitHub Desktop.
Save joharei/2a4765f8fc39878a669f29055c1af1bc to your computer and use it in GitHub Desktop.
FAB with show/hide animations according to the Material spec
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterExitState
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material.FloatingActionButtonDefaults
import androidx.compose.material.FloatingActionButtonElevation
import androidx.compose.material.MaterialTheme
import androidx.compose.material.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.material.FloatingActionButton as MaterialFloatingActionButton
/**
* FAB with show/hide animations according to the Material spec
*/
@Composable
fun FloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
visible: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
backgroundColor: Color = MaterialTheme.colors.secondary,
contentColor: Color = contentColorFor(backgroundColor),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
content: @Composable () -> Unit,
) {
AnimatedVisibility(
modifier = modifier,
visible = visible,
enter = fadeIn(
animationSpec = tween(
durationMillis = 15,
delayMillis = 30,
easing = LinearEasing,
),
),
exit = fadeOut(
animationSpec = tween(
durationMillis = 15,
delayMillis = 150,
easing = LinearEasing,
)
),
) {
val fabScale by transition.animateFloat(
transitionSpec = {
tween(
durationMillis = when (targetState) {
EnterExitState.PreEnter,
EnterExitState.Visible,
-> 330
EnterExitState.PostExit -> 135
},
delayMillis = 0,
easing = LinearOutSlowInEasing,
)
},
label = "FAB scale"
) {
when (it) {
EnterExitState.PreEnter,
EnterExitState.PostExit,
-> 0f
EnterExitState.Visible -> 1f
}
}
MaterialFloatingActionButton(
modifier = Modifier.graphicsLayer {
scaleX = fabScale
scaleY = fabScale
},
onClick = onClick,
interactionSource = interactionSource,
shape = shape,
backgroundColor = backgroundColor,
contentColor = contentColor,
elevation = elevation,
) {
val contentScale by transition.animateFloat(
transitionSpec = {
tween(
durationMillis = when (targetState) {
EnterExitState.PreEnter,
EnterExitState.Visible,
-> 240
EnterExitState.PostExit -> 135
},
delayMillis = when (targetState) {
EnterExitState.PreEnter,
EnterExitState.Visible,
-> 90
EnterExitState.PostExit -> 0
},
easing = FastOutLinearInEasing,
)
},
label = "FAB content scale"
) {
when (it) {
EnterExitState.PreEnter,
EnterExitState.PostExit,
-> 0f
EnterExitState.Visible -> 1f
}
}
Box(
Modifier.graphicsLayer {
scaleX = contentScale
scaleY = contentScale
}
) {
content()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment