강좌를 보면 RequestState 의 유용한 점에 대하여 알리고 있고, Request 을 State 로 처리하면서 compose 의 특징을 살려 처리해서 이용도가 높은 wrapper 으로 보입니다.
// https://gist.github.com/stevdza-san/cca20eff9f2c4c7d783ffd0a0061b352
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
sealed class RequestState<out T> {
data object Idle : RequestState<Nothing>()
data object Loading : RequestState<Nothing>()
data class Success<T>(val data: T) : RequestState<T>()
data class Error(val message: String) : RequestState<Nothing>()
fun isLoading() = this is Loading
fun isSuccess() = this is Success
fun isError() = this is Error
/**
* Returns data from a [Success].
* @throws ClassCastException If the current state is not [Success]
* */
fun getSuccessData() = (this as Success).data
fun getSuccessDataOrNull(): T? {
return try {
(this as Success).data
} catch (e: Exception) {
null
}
}
/**
* Returns an error message from an [Error]
* @throws ClassCastException If the current state is not [Error]
* */
fun getErrorMessage() = (this as Error).message
fun getErrorMessageOrNull(): String? {
return try {
(this as Error).message
} catch (e: Exception) {
null
}
}
@Composable
fun DisplayResult(
onIdle: (@Composable () -> Unit)? = null,
onLoading: @Composable () -> Unit,
onSuccess: @Composable (T) -> Unit,
onError: @Composable (String) -> Unit,
) {
AnimatedContent(
targetState = this,
transitionSpec = {
fadeIn(tween(durationMillis = 300)) togetherWith
fadeOut(tween(durationMillis = 300))
},
label = "Content Animation"
) { state ->
when (state) {
is Idle -> {
onIdle?.invoke()
}
is Loading -> {
onLoading()
}
is Success -> {
onSuccess(state.getSuccessData())
}
is Error -> {
onError(state.getErrorMessage())
}
}
}
}
}
4가지 상태를 가지며 1,2 상태는 Data를 가지지 않고 3,4 상태는 Data를 가집니다.
그리고 3가지 논리값을 가지는 검사함수가 포함됩니다.
그리고 2가지의 Data를 추출하는 함수가 있는데 2가지의 Data에 Null 이포함되는 형과 아닌형으로 총 4개의 Data 추출함수가 있습니다.
그리고 중요한것이 상태에 따라 적절한 Lamda 함수부를 호출하는 DisplayResult함수가 있고,
DisplayResult함수에서 첫번째인 Idle 상태는 선택사항입니다. 즉 idle 상태일때는 아무런 처리가 없어도 괜찮습니다.
생성되는 예입니다.
fun fetchDataWithWrapper(): Flow<RequestState<List<String>>>{
return flow {
emit(RequestState.Loading)
delay(2000)
emit(RequestState.Success(listOf("Testing","is","every thing")))
}
}
fun fetchDataWithWrapper(): Flow<RequestState<List<String>>>{
return flow {
emit(RequestState.Loading)
delay(2000)
emit(RequestState.Success(listOf("Testing","is","every thing")))
}
}
val data = repository.fetchDataWithWrapper()
val data = vm.data.collectAsState(initial = RequestState.Idle) // 초기값 선언
data.DisplayResult(
onLoading = { MainContent() },
onSuccess = { MainContent(text = data.getSuccessData().joinToString()) },
onError = { MainContent(text = data.getErrorMessage()) }
)
// 표시예
fun MainContent(
modifier: Modifier = Modifier,
text: String? = null
) {
Box(modifier = Modifier.fillMaxSize()) {
text?.let { Text(it) }
}//box
}//mainContent