[Android] Jetpack Compose에서 Toast Message를 Custom 할 수 있을까?

때론 디자인에 따라 Toast 위젯 디자인을 커스텀해야 하는 경우가 있습니다.

최근 사이드 프로젝트에서 Jetpack Compose로 Toast 위젯을 커스텀해야 하는 경우가 생겨

이번 기회에 알아보려고 합니다!


1. 기존에 사용한 Toast Message Custom

//Toast 위젯 상속
class CustomToast(context: Context) : Toast(context) {

}

//객체 생성
Toast(context).apply { 
	view = toastLayout
    setGravity(Gravity.CENTER,0,0)
}

 

기존에는 Toast 위젯를 상속받는 Class를 통해 Customview를 생성하거나, 객체를 생성하여 setView(), setGravitiy() 등 다양한 속성값을 줄 수 있었습니다.

하지만, SDK 30이상인 곳에서 setView()를 사용해 보면 Deprecated 경고가 뜨는 것을 확인할 수 있습니다.

 

내부 코드 설명을 확인해보면  Custom Toast View는 Deprecated 되었으며 대신에 SnackBar를 사용하라는 것을 확인할 수 있습니다. 

그렇다면 SnackBar를 이용하여 원하는 디자인대로 Toast를 커스텀해보도록 하겠습니다! 

그전에, Compose에서 SnackBar는 어떻게 사용하는지 간단하게 알아보도록 하겠습니다.

 

 

 

2.  Jetpack Compose SnackBar

Scaffold는 Material Application에서 사용되는 기본 구성 가능 함수입니다. Scaffold는 올바른 레이아웃 동작이 보장되는 화면을 구성하기 위해 여러 재료 구성 요소를 결합하는 간단한 방법을 제공합니다. 

Snackbar 또한 Scaffold를 이용하여 구현할 수 있습니다. 

@Composable
fun Scaffold(
    modifier: Modifier = Modifier,
    scaffoldState: ScaffoldState = rememberScaffoldState(),
    snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
)

Scaffold 내부를 통해 snackbarHost가 존재하는 것을 확인할 수 있습니다. 그렇다면 snackbarHost 내부도 알아보도록 하겠습니다.

@Composable
fun SnackbarHost(
    hostState: SnackbarHostState,
    modifier: Modifier = Modifier,
    snackbar: @Composable (SnackbarData) -> Unit = { Snackbar(it) }
)

 

Snackbar는 SnackbarHost를 이용하여 조작되며, SnackbarHostState를 넘겨주어 Snackbar의 State에 따라 동작된다는 것을 확인할 수 있습니다.  snackbar인자를 살펴보면 Composable를 매개변수로 받는 것을 통해 해당 부분을 통해 커스텀을 할 수 있다는 것을 추측할 수 있습니다! SnackbarData는 아래서 더 살펴보도록 하겠습니다.

 

 

 

2.  SnackBar Custom

Button를 눌렀을 때 Snackbar가 나오는 간단한 뷰를 만들어보도록 하겠습니다. 

@Composable
fun CustomSnackBarView() {
    Card(
        shape = RoundedCornerShape(10.dp),
        backgroundColor = Color.Yellow,
    ) {
        Text(
            text = "custom Toast Test",
            modifier = Modifier.padding(10.dp, 10.dp),
            textAlign = TextAlign.Center,
        )
    }
}

 

먼저 위 코드처럼 Custom하고 싶은 Composable를 만들어줍니다.

 val snackState = remember { SnackbarHostState() }
 val coroutineScope = rememberCoroutineScope()
 val scaffoldState = rememberScaffoldState()

    Scaffold(
        modifier = Modifier,
        scaffoldState = scaffoldState,
        snackbarHost = {
            SnackbarHost(
                hostState = snackState,
            ) { snackData ->
                TwoTooSnackBarView(snackData.message)
            }
        },
    ) { paddingValues ->
        Column(
            modifier = Modifier.fillMaxSize().padding(paddingValues),
        ) {
            Button(
                modifier = Modifier.fillMaxWidth(),
                onClick = {
                    coroutineScope.launch {
                        snackState.showSnackbar("Custom Snackbar")
                    }
                },
            ) {
                Text("Show Snackbar")
            }
        }
    }

그 후 Scaffold에 SnackbarHost를 생성하여 Custom 하고 싶은 컴포저블을 전달해 줍니다.

snackState.showSnackbar("Custom Snackbar")

위 코드는 Snackbar를 show 하는 코드로 버튼을 클릭하면 Snackbar가 노출되는 것을 확인할 수 있습니다. 

저는 showSnackbar()에 "Custom Snackbar"라는 내용을 전달하였는데요, 이 부분이 바로 전달할 View에 전달할 SnackbarData입니다! 원하는 message나 actionLabel, duration을 지정하여 함께 전달할 수 있습니다.

 

 

 

 

마무리

처음에 단순히 Toast로 접근하려고 했었지만 이 기회에 Compose에서 Snackbar를 사용해 볼 수 있었습니다! 여러분도 SDK에 맞게 적절한 커스텀 방식을 찾아 구현해 보면 좋을 것 같습니다:)