Skip to content

Instantly share code, notes, and snippets.

@L10n42
Created May 30, 2024 12:39
Show Gist options
  • Save L10n42/2815f5c5dde03b4e49b962d337b414ae to your computer and use it in GitHub Desktop.
Save L10n42/2815f5c5dde03b4e49b962d337b414ae to your computer and use it in GitHub Desktop.
Custom 3D Dialog Animation in Jetpack Compose.
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
/**
* A composable function that opens a dialog with a custom 3D animation and the given content.
*
* @param onDismiss A callback function to be executed when the dialog is dismissed.
* @param inAnimDuration The duration (in milliseconds) of the dialog's entry animation.
* @param outAnimDuration The duration (in milliseconds) of the dialog's exit animation.
* @param properties [DialogProperties] for further customization of this dialog's behavior.
* @param content The content to be displayed inside the dialog.
*/
@Composable
fun AnimatedDialog(
onDismiss: () -> Unit,
inAnimDuration: Int = 720,
outAnimDuration: Int = 450,
properties: DialogProperties = DialogProperties(),
content: @Composable (triggerDismiss: () -> Unit) -> Unit,
) {
val scope = rememberCoroutineScope()
var isDialogVisible by remember { mutableStateOf(false) }
val animationSpec = tween<Float>(if (isDialogVisible) inAnimDuration else outAnimDuration)
val dialogAlpha by animateFloatAsState(
targetValue = if (isDialogVisible) 1f else 0f,
animationSpec = animationSpec
)
val dialogRotationX by animateFloatAsState(
targetValue = if (isDialogVisible) 0f else -90f,
animationSpec = animationSpec
)
val dialogScale by animateFloatAsState(
targetValue = if (isDialogVisible) 1f else 0f,
animationSpec = animationSpec
)
val dismissWithAnimation: () -> Unit = {
scope.launch {
isDialogVisible = false
delay(outAnimDuration.toLong())
onDismiss()
}
}
LaunchedEffect(Unit) {
isDialogVisible = true
}
Dialog(
onDismissRequest = dismissWithAnimation,
properties = properties
) {
Box(
modifier = Modifier
.alpha(dialogAlpha)
.scale(dialogScale)
.graphicsLayer { rotationX = dialogRotationX },
content = {
content(dismissWithAnimation)
}
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment