A Template for ViewModel + Flow
Whether you’re using MVI or MVVN, ViewModel is now the best way to store application-related data.
ViewModel for Android can be implemented using the Lifecycle library and Kotlin Flow.
Related Dependencies
val lifecycleVersion = "2.4.0"
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion")
implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion")
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
abstract class BaseViewModel<ViewState, ViewEvent>(
initialViewState: ViewState,
application: Application
) : AndroidViewModel(application) {
private val _viewStateFlow: MutableStateFlow<ViewState> = MutableStateFlow(initialViewState)
val viewStateFlow = _viewStateFlow.asStateFlow()
private val _viewEventFlow: MutableSharedFlow<ViewEvent> = MutableSharedFlow()
val viewEventFlow = _viewEventFlow.asSharedFlow()
private var viewEventFlowValve: CompletableDeferred<Unit> = CompletableDeferred()
init {
_viewEventFlow.subscriptionCount
.map { it > 0 }
.distinctUntilChanged()
.onEach {
if (it) {
viewEventFlowValve.complete(Unit)
} else {
viewEventFlowValve = CompletableDeferred()
}
}
.launchIn(viewModelScope)
}
protected fun updateViewState(updateFunction: (ViewState) -> ViewState) {
_viewStateFlow.update {
updateFunction(it)
}
}
protected suspend fun sendViewEvent(event: ViewEvent) {
viewEventFlowValve.await()
_viewEventFlow.emit(event)
}
}