I have a scenario where I make a call to a method in ViewModel as a first statement in my Composable Function. I try to collect events from ViewModel's MutableStateFlow in LaunchedEffect()block of my Composable Function. The problem is the events emitted by ViewModel are not collected in my Composable Function. But when I add an UI element (Button)in Composable function and call ViewModel's method on button click all events are generated without any issue. But in my requirement, there is no need for me to have UI element. I will share my code related to Composable function ,ViewModel and other required info below, plz help in resolving my issue:
MyComposable Function :
@Composable
fun LaunchApp(
navController: NavHostController,
appLauncherViewModel: AppLauncherViewModel = get()
) {
var showProgress by rememberSaveable { mutableStateOf(false) }
val context = LocalContext.current
appLauncherViewModel.checkUserLogin()
LaunchedEffect(key1 = context) {
appLauncherViewModel.responseEvent.collect { event ->
println("Event is $event")
when (event) {
is Response.Login -> {
navigateToLogin(navController)
showProgress = false
}
is Response.Success -> {
val user = event.data as? User
println("Login User is $user")
user?.userEmail?.let { email ->
navigateToStore(navController, email)
}
showProgress = false
}
is Response.Error -> {
val errorMessage = event.errorMessage
showProgress = false
Toast.makeText(context, "Something went wrong Re-Launch app", Toast.LENGTH_LONG)
.show()
}
is Response.Loading -> {
showProgress = true
}
else -> {
println("Else")
}
}
}
}
if (showProgress) {
ProgressBar()
}
/* AppScaffold(
title = "Home",
onLogoutClick = {
}
) {
if (showProgress) {
ProgressBar()
}
Column() {
Button(onClick = { appLauncherViewModel.checkUserLogin() }
) {
Text(text = "Click me...")
}
}
}*/
}
MyViewModel:
class AppLauncherViewModel(private val myCartAuthenticationRepository: MyCartAuthenticationRepository) :
ViewModel(),
LifecycleObserver {
var responseEvent = MutableSharedFlow<Response<Any>>()
fun checkUserLogin() {
viewModelScope.launch {
try {
responseEvent.emit(Response.Loading)
val result = myCartAuthenticationRepository.isUserLoggedIn()
result?.let {
val receivedUser = myCartAuthenticationRepository.getCurrentUser()
receivedUser.collect { user ->
user?.let {
responseEvent.emit(Response.Success(it))
} ?: run {
responseEvent.emit(Response.Login)
}
}
} ?: run {
responseEvent.emit(Response.Login)
}
} catch (e: Exception) {
responseEvent.emit(Response.Error(e.message.toString()))
}
}
}
}
Response:
sealed class Response<out T> {
object Loading:Response<Nothing>()
data class Success<out T>(val data: T) : Response<T>()
data class SuccessList<out T>(val dataList: List<T>,val dataType: DataType) : Response<T>()
data class Error(val errorMessage:String):Response<Nothing>()
data class SuccessConfirmation(val successMessage:String):Response<Nothing>()
object SignOut : Response<Nothing>()
object Login: Response<Nothing>()
}
enum class DataType {
A,
B,
C,
D
}
Repository:
class MyCartAuthenticationRepositoryImpl() : MyCartAuthenticationRepository{
private val auth = FirebaseAuth.getInstance()
override fun getCurrentUser(): Flow<User?> = flow {
val currentUser = auth.currentUser
emit(currentUser?.toUser())
}
override suspend fun signIn(email: String, password: String): FirebaseUser? = try {
val signInResult = auth.signInWithEmailAndPassword(email, password).await()
signInResult.user
} catch (e: Exception) {
println("${e.printStackTrace()}")
null
}
override suspend fun signUp(email: String, password: String): FirebaseUser? = try {
val signUpResult = auth.createUserWithEmailAndPassword(email, password).await()
signUpResult.user
}catch (e: Exception) {
null
}
override suspend fun isUserLoggedIn(): FirebaseUser? = auth.currentUser
/* suspend fun override fun isUserLoggedIn(): FirebaseUser? = try{
auth.currentUser
}catch (e: Exception) {
null
}
*/
override fun signOut() : Boolean {
auth.signOut()
auth.currentUser?.let {
return false
}?:run{
return true
}
}
private fun FirebaseUser?.toUser(): User? {
return this?.let {
User(userEmail = it.email ?: "")
}
}
}
MainActivity:
@Composable
fun Navigator(navHostController: NavHostController) {
NavHost(navController = navHostController, startDestination = "appLauncherScreen") {
composable("appLauncherScreen") { LaunchApp(navController = navHostController) }
composable("loginScreen") { LoginScreen(navController = navHostController) }
composable("registrationScreen") { Register(navController = navHostController) }
composable("forgotPasswordScreen") { ForgotPassword(navHostController) }
composable(
"store/{emailId}",
arguments = listOf(navArgument("emailId") { type = NavType.StringType })
) { backStackEntry ->
backStackEntry.arguments?.getString("emailId")
?.let { email -> StoreList(email, navController = navHostController) }
}