A family of sharing operators (as well as a hot SharedFlow) are on their way to simplify the kind of workflow you're looking for (using Kotlin Flows).
In the meantime, it's true that flows are cold in nature (and thus you cannot really share them as-is), but they can nevertheless share a hot source to achieve what you need. I provided details on how to do this in this answer.
In short, the end result looks like this:
val original: Flow<String> = flowOf("aap", "noot", "mies", "wim", "zus","jet","weide","does")
// create an implicit hot BroadcastChannel, shared between collectors
// so that they each get all elements (which are each produced only once)
val sharedFlow = original.broadcastIn(scope).asFlow()
// create derived cold flows, which will subscribe (on collect) to the
// same hot source (BroadcastChannel)
val flow1 = sharedFlow.filter { it.length == 4 }
val flow2 = sharedFlow.filter { it.length == 3 }.map { it.toUppercase() }
flow1.collect { it -> println("Four letter: ${it}") }
flow2.collect { it -> println("Three letter: ${it}") }
(This is soon to be replaced by a SharedFlow.)
.filter {}.forEach {}? - shkschneiderFlows for the moments. Learn coroutines and then jump to flows - coroutineDispatcher