0

I have a ProfileViewModel where I load the user profile when the user lands on the screen. To achieve this, I use stateIn so that the profile is fetched automatically.

Now, I want to modify profileUiState to:

  1. Show isLoading = true before calling logoutApi.logout().

  2. Call logoutApi.logout().

  3. Show isLoading = false and reload the profile data after logout.

Here’s my current ViewModel:

class ProfileViewModel(
    private val userInfoApi: UserInfoApi,
    private val logoutApi: LogoutApi,
) : ViewModel() {

    private val eventChannel = Channel<ProfileUiEvent>()
    val events = eventChannel.receiveAsFlow()

    var profileUiState = flow {
        val result = userInfoApi.getUserInfo()
        emit(
            result.fold(
                onSuccess = {
                    ProfileUiState(userInfo = it, isLoading = false)
                },
                onFailure = {
                    ProfileUiState(errorMessage = it.message, isLoading = false)
                }
            )
        )
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.Lazily,
        initialValue = ProfileUiState(isLoading = true)
    )

    fun onAction(action: ProfileUiEvent) {
        viewModelScope.launch {
            eventChannel.send(action)
        }
    }

    fun logoutUser() {
        viewModelScope.launch {
             // update the logic in here for logout
            profileUiState
        }
    }
}

I want to avoid anti-patterns few of them and I read from this blog like :

  • Calling getUserInfo() inside init {} of viewmodel.

  • Using LaunchedEffect in Compose to trigger an API call.

How can I modify profileUiState properly to handle logout while keeping this approach?

0

1 Answer 1

0

------edit------

Example:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update

class ProfileViewModel(
    private val userInfoApi: UserInfoApi,
    private val logoutApi: LogoutApi,
) : ViewModel() {

    private val eventChannel = Channel<ProfileUiEvent>()
    val events = eventChannel.receiveAsFlow()

    private val _profileUiState = MutableStateFlow(ProfileUiState())
    val profileUiState = _profileUiState
        .onStart {
            val result = userInfoApi.getUserInfo()
            result.fold(
                onSuccess = { success ->
                    _profileUiState.update { ProfileUiState(userInfo = success, isLoading = false) }
                },
                onFailure = { failure ->
                    _profileUiState.update {
                        ProfileUiState(
                            errorMessage = failure.message,
                            isLoading = false
                        )
                    }
                }
            )
        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.Lazily,
            initialValue = ProfileUiState(isLoading = true)
        )

    fun onAction(action: ProfileUiEvent) {
        viewModelScope.launch {
            eventChannel.send(action)
        }
    }

    fun logoutUser() {
        viewModelScope.launch {
            // call the logout and handle the result
            _profileUiState.update { /*Update here the state*/ }
        }
    }
}

Now you have a State flow that when the collector is subscribed it calls the action inside the onstart and then you can update it whenever you like through the

_profileUiState
Sign up to request clarification or add additional context in comments.

6 Comments

Init function is not good when we need to test the viewmodel. Read more about it here proandroiddev.com/…
I agree but in your case you want to call an api in the initial load of the screen and then you need to modify the state after calling an other api.
Maybe you could try to separate the api call from the screen state and have an other MutableStateflow to observe the state. And keep the stateIn to load the data
Can you give me example please?
I edited my answer with an example
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.