LiveData transformations and mediators
package com.example.myapp.ui
import androidx.lifecycle.*
import kotlinx.coroutines.launch
class UserViewModel(
private val userRepository: UserRepository
) : ViewModel() {
private val _userId = MutableLiveData<Int>()
// Transform user ID to full user object
val user: LiveData<User?> = _userId.switchMap { id ->
liveData {
val user = userRepository.getUser(id)
emit(user)
}
}
// Map user to display name
val displayName: LiveData<String> = Transformations.map(user) { user ->
user?.let { "${it.firstName} ${it.lastName}" } ?: "Unknown"
}
// Combine multiple LiveData sources
private val _posts = MutableLiveData<List<Post>>()
private val _comments = MutableLiveData<List<Comment>>()
val feedItems: LiveData<FeedData> = MediatorLiveData<FeedData>().apply {
var posts: List<Post>? = null
var comments: List<Comment>? = null
fun update() {
if (posts != null && comments != null) {
value = FeedData(posts!!, comments!!)
}
}
addSource(_posts) { newPosts ->
posts = newPosts
update()
}
addSource(_comments) { newComments ->
comments = newComments
update()
}
}
// Conditional transformation
private val _searchQuery = MutableLiveData<String>()
val searchResults: LiveData<List<Post>> = _searchQuery.switchMap { query ->
if (query.isNullOrBlank()) {
liveData { emit(emptyList<Post>()) }
} else {
liveData {
val results = userRepository.searchPosts(query)
emit(results)
}
}
}
// Custom transformation with distinct
val postCount: LiveData<Int> = Transformations.distinctUntilChanged(
Transformations.map(_posts) { it?.size ?: 0 }
)
fun setUserId(id: Int) {
_userId.value = id
}
fun search(query: String) {
_searchQuery.value = query
}
}
data class FeedData(
val posts: List<Post>,
val comments: List<Comment>
)
LiveData transformations create derived data streams reactively. Transformations.map() converts values—like mapping User to username string. Transformations.switchMap() switches LiveData sources based on input, enabling dynamic queries. MediatorLiveData combines multiple LiveData sources, useful for aggregating data. The Mediator observes sources and updates when any changes. Transformations execute lazily only when observed. They maintain lifecycle awareness, preventing memory leaks. distinctUntilChanged() prevents duplicate emissions. These patterns eliminate manual observation management and enable declarative data flows. Transformations work with ViewModels to expose clean, transformed state to UI layers while keeping business logic separate.