Last active
September 2, 2022 14:10
-
-
Save AfzalivE/46e0460aacbd3c04268d4446b3a7f2b1 to your computer and use it in GitHub Desktop.
ExplodingFab Jetpack Compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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() | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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