Use Kotlinx Coroutines Channel
to send and receive events between Fragments.
Author: Petrus Nguyễn Thái Học
sealed interface MainSingleEvent<out T : MainSingleEvent<T>> {
interface Key<out T : MainSingleEvent<T>>
val key: Key<T>
data class HomeFragmentResult(val text: String) : MainSingleEvent<HomeFragmentResult> {
override val key = HomeFragmentResult
companion object : Key<HomeFragmentResult>
}
data class DashboardFragmentResult(val text: String) : MainSingleEvent<DashboardFragmentResult> {
override val key = DashboardFragmentResult
companion object : Key<DashboardFragmentResult>
}
data class HomeDetailsResult(val text: String) : MainSingleEvent<HomeDetailsResult> {
override val key = HomeDetailsResult
companion object : Key<HomeDetailsResult>
}
companion object {
val KEYS: Set<Key<SomeMainSingleEvent>> = setOf(
HomeFragmentResult,
DashboardFragmentResult,
HomeDetailsResult,
)
}
}
class MainVM : ViewModel() {
private val eventChannels: Map<MainSingleEvent.Key<SomeMainSingleEvent>, Channel<SomeMainSingleEvent>> =
MainSingleEvent.KEYS.associateBy(
keySelector = { it },
valueTransform = { Channel<MainSingleEvent<SomeMainSingleEvent>>(Channel.UNLIMITED) }
)
fun <T : MainSingleEvent<T>> sendEvent(event: T) {
checkNotNull(eventChannels[event.key]) { "Must register ${event.key} in MainSingleEvent.Companion.KEYS before using!" }
.trySend(event)
.getOrThrow()
.also { Log.d("@@@", "Sent $event") }
}
@Suppress("UNCHECKED_CAST")
fun <T : MainSingleEvent<T>, K : MainSingleEvent.Key<T>> receiveEventFlow(key: K): Flow<T> =
checkNotNull(eventChannels[key]) { "Must register $key in MainSingleEvent.Companion.KEYS before using!" }
.receiveAsFlow()
.map { it as T }
}
We will share MainVM
instance between Fragment
s using Activity
as owner.
private val mainVM by viewModels<MainVM>(
ownerProducer = { requireActivity() }
)
// send in HomeFragment
mainVM.sendEvent(MainSingleEvent.HomeFragmentResult("Hello from HomeFragment"))
// receive in others
mainVM.receiveEventFlow(MainSingleEvent.HomeFragmentResult)
.onEach { Log.d("###", "Received $it") }
.launchIn(lifecycleScope)