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 Fragments 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)