[Compose] Snackbar 사용하기

WonDDak·2023년 1월 2일
0

Compose

목록 보기
2/5

Compose에서 SnackBar사용하기

compose에서 snackbar을 사용하기 위해서는 scaffold를 이용해야한다.

scaffold는 여기서는 자세히 다루지는 않지만 TopBar, BottomBar, Snackbar, FloatingActionButton, Drawer 을 사용할수 있도록 미리 세팅 되어있는 화면이라 생각 하시면 됩니다.

기본적인 사용법

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
        
            val scaffoldState = rememberScaffoldState()
            val scope = rememberCoroutineScope()

            Scaffold(scaffoldState = scaffoldState) {
                Button(onClick = {
                    scope.launch {
                        scaffoldState.snackbarHostState.showSnackbar("hello")
                    }
                }) {
                    Text(text = "hello")
                }
            }

        }
    }
}

기본적으로는 위와같이 scaffoldState을 저장하고,
특정액션이 일어날때 scaffoldState.snackbarHostState.showSnackbar 함수를 호출해주면 됩니다.
showSnackbar는 suspend 함수이므로, 코루틴으로 감싼 모습입니다.

다른 옵션

showSnackbar 에는 기본적으로 message를 필수로 받으며 다른 옵션이 있습니다.

label과 duration

추가로 actionLabel(String)과 duration을 지정가능합니다.

duration은 SnackbarDuration 타입이며 다음과 같이 3가지가 있습니다.

  • SnackbarDuration.Short
  • SnackbarDuration.Long
  • SnackbarDuration.Indefinite

Indefinite옵션의 경우 label을 클릭하기 전까지는 닫히지 않습니다.

 scaffoldState.snackbarHostState.showSnackbar(
 	message = "hello",
	actionLabel = "Done",
	duration = SnackbarDuration.Indefinite
)


label과 duration이 지정된 모습

snackBarResult

showSnackbar은 기본적으로 SnackbarResult를 반환합니다.
즉 다음과 같이 결과값을 이용하여 액션처리를 가능합니다.

val snackBarResult = scaffoldState.snackbarHostState.showSnackbar(
    message = "hello",
    actionLabel = "Done",
    duration = SnackbarDuration.Indefinite
)
when (snackBarResult) {
    SnackbarResult.Dismissed -> {
       //스낵바가 사라질때 액션
    }
    SnackbarResult.ActionPerformed -> {
        //버튼을 눌렀을때 액션
    }
}

편하게 사용..?

snackbar을 호출하기 위해서는 scaffoldState으로부터 showSnackbar을 호출해야 하므로 뷰가 많아지면 호출하기가 힘들어집니다.

viewmodel을 이용하여 제가 사용하고있는 방식을 공유드립니다..(참고만)

SnackBarItem 생성

data class SnackBarItem(
    val show: Boolean = false,
    val message: String = "",
    val actionLabel: String? = null,
    val duration: SnackbarDuration = SnackbarDuration.Short,
    val performAction: () -> Unit = {},
)

Viewmodel 생성

open class SnackBarController(

) : ViewModel() {
    var snackBarMessage: SnackBarItem by mutableStateOf(SnackBarItem())
        private set

    private fun showSnackBar(snackBarItem: SnackBarItem) {
        snackBarMessage = snackBarItem
    }

    fun showSnackBar(message: String) {
        showSnackBar(
            message = message,
            label = null
        )
    }

    fun showSnackBar(message: String, label: String?) {
        showSnackBar(
            message = message,
            label = label,
            duration = SnackbarDuration.Short
        )
    }

    fun showSnackBar(message: String, duration: SnackbarDuration) {
        showSnackBar(
            message = message,
            label = null,
            duration = duration
        )
    }

    fun showSnackBar(
        message: String, label: String?, action: () -> Unit,
    ) {
        showSnackBar(
            message = message,
            label = label,
            duration = SnackbarDuration.Short,
            action = action
        )
    }

    fun showSnackBar(message: String, label: String?, duration: SnackbarDuration) {
        showSnackBar(
            message = message,
            label = label,
            duration = duration,
            action = {}
        )
    }

    fun showSnackBar(
        message: String,
        label: String?,
        duration: SnackbarDuration,
        action: () -> Unit,
    ) {
        showSnackBar(
            SnackBarItem(
                true,
                message = message,
                actionLabel = label,
                duration = duration,
                performAction = action
            )
        )
    }

    fun resetSnackBar() {
        snackBarMessage = SnackBarItem()
    }
}

setcontent 부분을 수정

class MainActivity : ComponentActivity() {
    private val snackBarViewModel: SnackBarController by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val scaffoldState = rememberScaffoldState()
            LaunchedEffect(snackBarViewModel.snackBarMessage) {
                snackBarViewModel.snackBarMessage.let {
                	// show가 true일때만 실행됩니다.
                    if (it.show) {
                        val snackBarResult = scaffoldState.snackbarHostState.showSnackbar(
                            it.message,
                            it.actionLabel,
                            it.duration
                        )
                        when (snackBarResult) {
                            SnackbarResult.Dismissed -> {
                                snackBarViewModel.resetSnackBar()
                            }
                            SnackbarResult.ActionPerformed -> {
                                it.performAction()
                            }
                        }
                    }
                }
            }
            Scaffold(scaffoldState = scaffoldState) {
                Button(onClick = {
                    snackBarViewModel.showSnackBar(
                        "hello",
                        "Done",
                        SnackbarDuration.Indefinite)
                    {
                        this.finish()
                    }
                }) {
                    Text(text = "hello")
                }
            }

        }
    }
}

LaunchEffect는 설정할 key값이 바뀔때마다 취소하고 재실행되는 부분입니다.

이런식으로 해두면 하위 view가 많아져도 viewmodel로부터 showSnackBar 를 호출하여 처리가 가능합니다.


수정한 코드로 작동하는 모습

이상입니다.

profile
안녕하세요. 원딱입니다.

0개의 댓글