1

I started on building Apps using Android Compose and am learning Kotlin's coroutine and stuff. Below are the samples of code that I have written for an app, where I tried to use MutableSharedFlow emit and collectLatest for updating UI. Init Method of ViewModel

init {
    try {
        viewModelScope.launch {
            _state.value = state.value.copy(
                isSearchStarted = true
            )
            userCredRepository.getCred().also {security ->
                if (security?.authToken.isNullOrEmpty()){
                    println("RECEIVED NULL TOKEN")
                    delay(100)
                    _eventFlow.emit(LoginSignUpEvent.ErrorEvent("Please Login to continue", ""))
                    _state.value = state.value.copy(
                        isSearchStarted = false
                    )
                }else{
                    delay(100)
                    _eventFlow.emit(LoginSignUpEvent.SuccessEventLogIn("Subscribing"))
                }

            }
        }
    }catch (ex: CancellationException){
        println(ex.message)
    }catch (ex: Exception){
       println(ex.message)
    }
}

This is the view modal for the Login SingUp screen. Here userCredRepository communicates with sqlite database, and fetches the token on the app startup.

Login SignUp Screen

 LaunchedEffect(true) {
    loginSignUpViewModal.eventFlow.collectLatest { event ->
        when (event) {
            is LoginSignUpEvent.ErrorEvent -> {
                state.snackbarHostState.showSnackbar(
                    message = event.message!!,
                    actionLabel = event.actionMessage
                )
            }
            is LoginSignUpEvent.SuccessEventSignUp -> {
                state.snackbarHostState.showSnackbar(
                    message = event.message!!,
                    actionLabel = event.actionMessage
                )
            }
            is LoginSignUpEvent.SuccessEventLogIn -> {
                navController.popBackStack()
                navController.navigate(NotesScreen.HomeScreen.name)
            }
        }
    }
}

This is sample code of Login Sign Up Screen where I listen to MutableSharedFlow's emitted values. This is the first screen that is displayed when the app runs ,and navigates to another screen if token is present. So the issue here is that the emitted values are not caught until I add dely(100) (as in the first code sample line 10 & 16 from start) before emitting values in the ViewModal. I have attached the link to the project.Link to project

Below is the updated code using Channel instead of Mutable shared flow, here it works well.

private val _eventFlow = Channel<LoginSignUpEvent>()
val eventFlow = _eventFlow.receiveAsFlow()

val toggleLoginSignupScreen = mutableStateOf(false)
var passwordVisibility = mutableStateOf(false)

init {
    try {
        viewModelScope.launch {
            _state.value = state.value.copy(
                isSearchStarted = true
            )
            userCredRepository.getCred().also {security ->
                if (security?.authToken.isNullOrEmpty()){
                    _eventFlow.send(LoginSignUpEvent.ErrorEvent("Please Login to continue", ""))
                    _state.value = state.value.copy(
                        isSearchStarted = false
                    )
                }else{
                    _state.value = state.value.copy(
                        isSearchStarted = false,
                    )
                    _eventFlow.send(LoginSignUpEvent.SuccessEventLogIn("Subscribing"))
                }

            }
        }
    }catch (ex: CancellationException){
        println(ex.message)
    }catch (ex: Exception){
       println(ex.message)
    }
}
  • why are you using `collectLatest`? instead of `collect`? – kingston May 15 '22 at 16:49
  • Hi @kingston collect will collect every value , and collectLatest will stop current work to collect latest value, but using collect doesn't solve it. I had tried that. – Sachet Wasti May 15 '22 at 17:19
  • 1
    If `getCred` does not suspend, then it's expected behaviour: `LaunchedEffect` is getting launched after `viewModelScope.launch`, so at time of `_eventFlow.emit`, `collectLatest` has not yet being run. Try adding logs to check this theory – Phil Dukhov May 15 '22 at 17:23
  • Check this video. This info might help. https://youtu.be/QNrNKPKe5oc – Abhimanyu May 16 '22 at 10:47
  • I have same issue. @PhilDukhov then how should we run api calls on just entering to the creen? Given that result should be prosessed as a single event, not as state – Waldmann Sep 04 '22 at 05:42

0 Answers0