Skip to content

Instantly share code, notes, and snippets.

@AfzalivE
Last active September 2, 2022 14:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AfzalivE/46e0460aacbd3c04268d4446b3a7f2b1 to your computer and use it in GitHub Desktop.
Save AfzalivE/46e0460aacbd3c04268d4446b3a7f2b1 to your computer and use it in GitHub Desktop.
ExplodingFab Jetpack Compose
// Using Jetpack Compose Transition v1
enum class FabState {
Initial,
Normal,
Exploded,
}
val fabSizeKey = DpPropKey()
val fabColorKey = ColorPropKey()
val fabIconAlphaKey = FloatPropKey()
@Composable
fun ExplodingFab(
onClick: () -> Unit,
icon: @Composable () -> Unit
) {
val fabState = remember { mutableStateOf(FabState.Initial) }
val primaryColor = MaterialTheme.colors.primary
val explodedColor = Color.White
val fabTransitionDef = transitionDefinition<FabState> {
state(FabState.Initial) {
this[fabSizeKey] = 20.dp
this[fabColorKey] = primaryColor
this[fabIconAlphaKey] = 0f
}
state(FabState.Normal) {
this[fabSizeKey] = 56.dp
this[fabColorKey] = primaryColor
this[fabIconAlphaKey] = 1f
}
state(FabState.Exploded) {
this[fabSizeKey] = 1500.dp
this[fabColorKey] = explodedColor
this[fabIconAlphaKey] = 0f
}
transition(FabState.Normal to FabState.Exploded) {
fabSizeKey using keyframes {
durationMillis = 500
58.dp at 0
48.dp at 200
1500.dp at 500
}
fabColorKey using keyframes {
durationMillis = 500
primaryColor at 300
explodedColor at 500
}
}
transition(FabState.Exploded to FabState.Normal) {
fabSizeKey using tween(durationMillis = 500, easing = FastOutSlowInEasing)
fabColorKey using tween(durationMillis = 500, easing = FastOutSlowInEasing)
}
}
val toState = if (fabState.value == FabState.Normal) FabState.Exploded else FabState.Normal
val transitionState = transition(
definition = fabTransitionDef,
initState = fabState.value,
toState = toState,
onStateChangeFinished = {
if (it == FabState.Exploded) onClick()
}
)
Fab(fabState = fabState, transitionState = transitionState, icon = icon)
}
@Composable
fun Fab(
fabState: MutableState<FabState>,
transitionState: TransitionState,
icon: @Composable () -> Unit
) {
FloatingActionButton(
onClick = {
fabState.value = if (fabState.value == FabState.Normal) {
FabState.Exploded
} else {
FabState.Normal
}
},
backgroundColor = transitionState[fabColorKey],
modifier = Modifier.size(transitionState[fabSizeKey])
) {
Box(
modifier = Modifier.alpha(transitionState[fabIconAlphaKey])
) {
icon()
}
}
}
// Using Jetpack Compose Transition v2
fun <S> Transition.States<S>.transitioning(pair: Pair<S, S>): Boolean {
return this.initialState == pair.first && this.targetState == pair.second
}
data class FabState(val fabColor: Color, val fabSize: Dp, val fabIconAlpha: Float)
@Composable
fun ExplodingFab(
onClick: () -> Unit,
icon: @Composable () -> Unit
) {
val primaryColor = MaterialTheme.colors.primary
val explodedColor = Color.White
val initialState = FabState(fabColor = primaryColor, fabSize = 20.dp, fabIconAlpha = 0f)
val normalState = FabState(fabColor = primaryColor, fabSize = 56.dp, fabIconAlpha = 1f)
val explodedState = FabState(fabColor = explodedColor, fabSize = 1500.dp, fabIconAlpha = 0f)
var fabState by remember { mutableStateOf(initialState) }
val transition = updateTransition(
targetState = fabState,
onFinished = {
if (it == explodedState) onClick()
}
)
val fabColor by transition.animateColor(
transitionSpec = {
if (it.transitioning(initialState to normalState)) {
tween(durationMillis = 500)
} else {
tween(durationMillis = 500, delayMillis = 300)
}
}
) { it.fabColor }
val fabSize by transition.animateDp(
transitionSpec = {
if (it.transitioning(initialState to normalState)) {
tween(durationMillis = 100)
} else {
keyframes {
durationMillis = 500
58.dp at 0
48.dp at 200
1500.dp at 500
}
}
}
) { it.fabSize }
val fabIconAlpha by transition.animateFloat { it.fabIconAlpha }
Fab(
backgroundColor = fabColor,
alpha = fabIconAlpha,
fabSize = fabSize,
icon = icon,
onClick = {
fabState = if (fabState == normalState) {
explodedState
} else {
normalState
}
}
)
onActive {
fabState = normalState
}
}
@Composable
fun Fab(
onClick: () -> Unit,
backgroundColor: Color,
fabSize: Dp,
alpha: Float,
icon: @Composable () -> Unit
) {
FloatingActionButton(
onClick = onClick,
backgroundColor = backgroundColor,
modifier = Modifier.size(fabSize)
) {
Box(
modifier = Modifier.alpha(alpha)
) {
icon()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment