본문 바로가기

안드로이드/Compose Dialog

[안드로이드 Jetpack Compose Dialog 시리즈] Loading Dialog 구현하기 #3


안녕하세요. 이번 포스팅에서는 안드로이드 Jetpack Compose에서 전역으로 사용할 수 있는 로딩 팝업창을 띄우는 방법에 대해 포스팅하겠습니다. 로딩을 보여주는 방법은 무수히 많지만, 저는 xml 시절부터 안드로이드에서 제공하는 기본 dialog를 상속받아서 구현하는 형태로 많이 사용했습니다.

왜냐하면 dialog에는 dismissOnBackPress, dismissOnClickOutSide 등 기본으로 제공하는 기능이 많아서 개발자가 따로 구현하지 않아도 되기 때문입니다. Compose에서도 비슷한 방법으로 간단하게 로딩창을 보여줄 수 있습니다.

전역 로딩 팝업창
전역 로딩 팝업창

1. Loading Dialog 만들기

전역 로딩 팝업창을 만들기 위해 먼저 LoadingState라는 싱글턴 객체를 만들어 로딩 상태를 관리합니다. 그런 다음, Compose의 Dialog 컴포저블을 사용하여 GlobalLoadingScreen을 구현합니다.

// CustomLoadingDialogState.kt
object LoadingState {
    private val _isLoading = MutableStateFlow(false) // 초기값을 false로 설정
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    fun show() {
        _isLoading.value = true
    }

    fun hide() {
        _isLoading.value = false
    }
}
// CustomLoadingDialog.kt
@Composable
fun GlobalLoadingScreen() {
    val isLoading = LoadingState.isLoading.collectAsState().value

    if (isLoading) {
        Dialog(
            onDismissRequest = { LoadingState.hide() },
            properties = DialogProperties(
                dismissOnBackPress = true,
                dismissOnClickOutside = true,
            )
        ) {
            CircularProgressIndicator()
        }
    }
}

2. Loading Dialog 사용하기

로딩 팝업창을 전역에서 사용하기 위해서는 앱의 최상위 Composable 함수에 GlobalLoadingScreen을 추가해야 합니다. 이렇게 구현하면 어느 곳에서든 LoadingState.show()와 LoadingState.hide()를 호출하여 로딩 화면을 표시하거나 숨길 수 있습니다. 예를 들어, MainActivity의 setContent 내에 GlobalLoadingScreen을 추가합니다:

// MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            val navController = rememberNavController()
            CleanarchitectureexampleTheme {
                Box {
                    MainGraph(mainNavController = navController)
                    GlobalLoadingScreen()
                }
            }
        }
    }
}

그리고 아래와 같은 형태로 LoadingState.show()를 호출하면 로딩 팝업창이 나오게 됩니다.

Button(
    onClick = { LoadingState.show() },
    shape = RectangleShape,
) {
    Text(
        text = "3. Show LoadingDialog",
        textAlign = TextAlign.Center,
        style = TextStyle(
            fontSize = 14.sp,
            fontWeight = FontWeight.Bold
        )
    )
}

3. Loading Dialog 정리

이 구현 방법의 장점은 로딩 상태의 관리와 UI 표시를 분리한다는 것입니다. LoadingState 객체를 사용하여 애플리케이션의 어느 곳에서나 로딩 상태를 제어할 수 있으며, GlobalLoadingScreen은 이 상태에 반응하여 사용자에게 로딩 인디케이터를 보여줍니다.

또한, Dialog를 통해 구현하면 DialogProperties 등의 속성을 통해서 뒤로 가기 버튼이나 바깥쪽 클릭으로 다이얼로그를 닫을 수 있는 옵션을 기본으로 사용할 수 있습니다. 개발자가 직접 신경써야하는 부분도 기본 Dialog에 있기 때문에 편리하고, 필요에 따라 이러한 속성을 조정하여 로딩 다이얼로그의 동작을 수정할 수 있습니다.

이번 포스팅에서는 간단하게 전역 로딩 팝업창을 구현해봤는데, 해당 내용을 참고해서 원하는 형태의 로딩 팝업창을 구현하는데 도움이 됐으면 좋겠습니다.