[Android] Compose Dialog Animation
개요
Compose를 활용할 때, Dialog를 활용하기 위해서 Dialog 함수를 많이 사용합니다. 또한, Animation을 적용하기 위해서 AnimatedVisibility 함수를 사용하면 아마 코드가 이런 식으로 작성될 것이라고 생각됩니다.
val openDialog = remember { mutableStateOf(false) }
AnimatedVisibility(
visible = openDialog.value,
...
) {
Dialog(onDismissRequest = {}) {
Text("Test")
}
}
출처 : https://stackoverflow.com/questions/72527630/animatedvisibility-not-working-with-dialog
다만, Dialog 함수의 특수성으로 인하여 위와 같이 코드를 작성하면 Dialog에 AnimatedVisibility의 애니메이션이 적용되지 않습니다. 자세한 이유를 알 수 없지만, Stackoverflow에서 한 유저의 답변에 따르면 'Dialog 함수는 순수한 Composable 함수가 아니라서 적용이 되지 않는다.'라고 합니다. 아무래도 Dialog 함수 안에 Modifier가 정의되어 있지 않은 것을 보면, 내부는 잘 모르겠지만, 어느 정도 신뢰가 가는 답변이라고 생각됩니다. 어쨌든, 확실한 건 애니메이션이 적용되지 않는다는 것입니다.
해결 방법
1. Dialog를 사용하지 않고 새로운 Composable 함수를 커스텀한다.
문제는 AnimatedVisibility가 아닌 Dialog 함수에 있습니다. 그렇기 때문에 Dialog를 사용하지 않고 다른 Composable 함수를 적용하면 쉽게 해결할 수 있습니다. (그게 무슨 해결 방법이냐고 말 할수도 있지만..) 실제로 제가 만든 Dialog라고 이름 붙인 Composable 함수는 이렇게 생겼습니다.
@Composable
fun BasicDialog(
modifier: Modifier = Modifier,
title: String = "",
description: String = "",
negativeText: String = "",
onClickNegative: () -> Unit = {},
positiveText: String = "",
onClickPositive: () -> Unit = {},
containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
containerShape: Shape = RoundedCornerShape(8.dp),
titleTextStyle: TextStyle = LocalTextStyle.current,
descriptionTextStyle: TextStyle = LocalTextStyle.current,
buttonTextStyle: TextStyle = LocalTextStyle.current
) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.fillMaxWidth(0.8f)
.wrapContentHeight()
.background(
color = containerColor,
shape = containerShape
)
.padding(16.dp),
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = title,
textAlign = TextAlign.Center,
style = titleTextStyle
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = description,
style = descriptionTextStyle
)
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
TextButton(
onClick = {
onClickNegative()
}
) {
Text(
text = negativeText,
style = buttonTextStyle
)
}
TextButton(
onClick = {
onClickPositive()
}
) {
Text(
text = positiveText,
style = buttonTextStyle
)
}
}
}
}
}
이런 식으로 직접 정의해서 사용했습니다.
2. 라이브러리를 사용한다.
이와 관련해서 이미 라이브러리로 구현되어 있습니다. 제가 정의한 라이브러리를 하나 소개합니다.
https://github.com/kumgold/animated-dialog
라이브러리를 통해서 Animation이 적용된 Dialog를 쉽게 사용할 수 있습니다. 사용방법은 기존 Dialog 함수와 비슷합니다. MutableStateFlow Boolean 값을 그대로 ScaleDilaog에 전달한 후 Boolean 값에 따라 함수가 생성되거나 제거됩니다.
ScaleDialog 함수의 모습
@Composable
fun ScaleDialog(
dialogState: MutableState<Boolean>,
durationMilli: Int = 300,
backgroundColor: Color = Color.Black.copy(alpha = 0.8f),
title: String = "",
description: String = "",
negativeText: String = "",
onClickNegative: () -> Unit = {},
positiveText: String = "",
onClickPositive: () -> Unit = {},
containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
containerShape: Shape = RoundedCornerShape(8.dp),
titleTextStyle: TextStyle = LocalTextStyle.current,
descriptionTextStyle: TextStyle = LocalTextStyle.current,
buttonTextStyle: TextStyle = LocalTextStyle.current,
) {
...
}
실제 프로젝트에서 ScaleDialog를 사용하는 모습
val showDialog = remember { mutableStateOf(false) }
ScaleDialog(
dialogState = showDialog
)
실제 프로젝트에서 SlideDialog를 사용하는 모습
val showDialog = remember { mutableStateOf(false) }
SlideDialog(
dialogState = showDialog
)
여기까지 골드였습니다.
감사합니다.