https://kotlinlang.org logo
#compose
Title
# compose
a

aoriani

02/25/2022, 3:13 AM
I am implementing a barcode scanner in Compose. Once it scans a barcode, it needs to do a network request to find the respective item. If an item is found I dismiss the scanner. If it is not found or some error happens, a dialog is shown. In order to accomplish that I think that I will need to do something along these lines:
Copy code
data class DialogState(val isShown: Boolean = false, val message: String = "")

@Composable
fun Screen(navController: NavController, viewModel: MyViewModel) {
    val dialogState by rememberUpdateState(DialogState)
    val scope = rememberCoroutineScope()
    Button(onClick = { scope.launch { 
        when (val result = viewModel.doRequest()) {
           is Success -> navController.popBackStack()
           is NotFound -> dialogState = DialogState(true, "Not Found")
           else -> dialogState = DialogState(true, "error")
   }})
   if (dialogState.isShown) {
       AlertDialog(.......) 
   } 
          
}
Would this be correct ?
e

Eric Chee

02/25/2022, 3:34 AM
You more or less got the right idea. One suggestion though. i’m gonna assume your viewModel.doRequest() is a suspend function since you wrapped it in a coroutinescope. I would recommend NOT to expose public suspendable functions in your ViewModel to your view. Instead I would recommend using a regular function in the viewmodel and in the implementation of ViewModel, the function uses viewModelScope. And from there you can expose a flow/livedata/rxstream/MutableState for the Composable to observe. Psuedo code
Copy code
class ViewModel {
    val resultFlow: StateFLow<Result>
     
    fun doRequest() { viewmodelScope.launch{ 
        val result = somerequest()
        resultFlow.value = result
    }
}
Copy code
@Composable
fun ...() {
    val result viewModel.resultFLowObserveAsState()
    when (result) {
       ....
    }
}
☝🏻 1
Therefore you wont need a scope for your Composable function, and your view is now free to fire as many requests as needed to the viewmodel, while not worrying about the behind the scenes of how your ViewModel fetches data, async or not
and if you get an error, you can wrap your call in a runCatching{} block and expose a Failed state in either a separate error flow, or through a sealed class in your main viewmodel state flow
z

Zach Klippenstein (he/him) [MOD]

03/03/2022, 4:05 AM
Your
rememberUpdatedState
looks a bit suspicious - are you passing it a companion object? It's definitely not a parameter, which is basically all that function is intended for
a

aoriani

03/05/2022, 2:21 AM
@Zach Klippenstein (he/him) [MOD] It has a typo. Pease read it as
Copy code
val dialogState by rememberUpdateState(DialogState())
The initial state is a new
DialogState
object created with default values for the params
11 Views