abstractclassBaseViewModel<Event : UiEvent, State : UiState, Effect : UiEffect> : ViewModel() { // Create Initial State of View privateval initialState : State by lazy { createInitialState() } abstractfuncreateInitialState() : State
// Get Current State val currentState: State get() = uiState.value
privateval _uiState : MutableStateFlow<State> = MutableStateFlow(initialState) val uiState = _uiState.asStateFlow()
privateval _event : MutableSharedFlow<Event> = MutableSharedFlow() val event = _event.asSharedFlow()
// Events that user performed sealedclassEvent : UiEvent { object OnRandomNumberClicked : Event() object OnShowToastClicked : Event() }
// Ui View States dataclassState( val randomNumberState: RandomNumberState ) : UiState
// View State that related to Random Number sealedclassRandomNumberState{ object Idle : RandomNumberState() object Loading : RandomNumberState() dataclassSuccess(val number : Int) : RandomNumberState() }
/** * Generate a random number */ privatefungenerateRandomNumber() { viewModelScope.launch { // Set Loading setState { copy(randomNumberState = MainContract.RandomNumberState.Loading) } try { // Add delay for simulate network call delay(5000) val random = (0..10).random() if (random % 2 == 0) { // If error happens set state to Idle // If you want create a Error State and use it setState { copy(randomNumberState = MainContract.RandomNumberState.Idle) } throw RuntimeException("Number is even") } // Update state setState { copy(randomNumberState = MainContract.RandomNumberState.Success(number = random)) } } catch (exception : Exception) { // Show error setEffect { MainContract.Effect.ShowToast } } } }
// Collect ui state lifecycleScope.launchWhenStarted { viewModel.uiState.collect { when (it.randomNumberState) { is MainContract.RandomNumberState.Idle -> { binding.progressBar.isVisible = false } is MainContract.RandomNumberState.Loading -> { binding.progressBar.isVisible = true } is MainContract.RandomNumberState.Success -> { binding.progressBar.isVisible = false binding.number.text = it.randomNumberState.number.toString() } } } }
// Collect side effects lifecycleScope.launchWhenStarted { viewModel.effect.collect { when (it) { is MainContract.Effect.ShowToast -> { binding.progressBar.isVisible = false // Simple method that shows a toast showToast("Error, number is even") } } } }