package com.example.myapp.ui.detail
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class PostDetailViewModel @Inject constructor(
private val repository: PostRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
private val postId: Int = savedStateHandle.get<Int>("postId") ?: 0
private val _uiState = MutableStateFlow(PostDetailUiState())
val uiState: StateFlow<PostDetailUiState> = _uiState.asStateFlow()
init {
loadPost()
loadComments()
}
private fun loadPost() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
repository.getPost(postId)
.onSuccess { post ->
_uiState.update {
it.copy(
post = post,
isLoading = false,
isLiked = post.likedByCurrentUser
)
}
}
.onFailure { e ->
_uiState.update {
it.copy(error = e.message, isLoading = false)
}
}
}
}
private fun loadComments() {
viewModelScope.launch {
repository.getComments(postId)
.collect { comments ->
_uiState.update { it.copy(comments = comments) }
}
}
}
fun toggleLike() {
viewModelScope.launch {
val currentState = _uiState.value
// Optimistic update
_uiState.update {
it.copy(
isLiked = !currentState.isLiked,
likesCount = if (currentState.isLiked)
currentState.likesCount - 1
else
currentState.likesCount + 1
)
}
repository.toggleLike(postId)
.onFailure {
// Rollback on error
_uiState.update {
it.copy(
isLiked = currentState.isLiked,
likesCount = currentState.likesCount
)
}
}
}
}
fun addComment(text: String) {
viewModelScope.launch {
repository.createComment(postId, text)
.onSuccess {
loadComments()
}
.onFailure { e ->
_uiState.update { it.copy(error = e.message) }
}
}
}
}
data class PostDetailUiState(
val post: Post? = null,
val comments: List<Comment> = emptyList(),
val isLoading: Boolean = false,
val isLiked: Boolean = false,
val likesCount: Int = 0,
val error: String? = null
)
MVVM separates concerns in Android apps—Model holds data, View displays UI, ViewModel mediates with business logic. ViewModels survive configuration changes, preventing data loss during rotation. I use ViewModel class with viewModelScope for coroutines. LiveData or StateFlow expose observable state to UI. Views observe changes with observe() or collectAsState(). Repository pattern abstracts data sources—network and database. Dependency injection with Hilt provides ViewModels. The ViewModel never holds Activity/Fragment references to avoid memory leaks. Use SavedStateHandle for process death survival. This architecture makes code testable, maintainable, and follows Android's recommended patterns.