https://kotlinlang.org logo
#coroutines
Title
# coroutines
e

Eslam Hussein

10/09/2023, 6:42 AM
Hello Everyone, I wander if passing a flow or stateflow as parameter in a method or class is good practice ?
Copy code
class A(private val anyState:StateFlow<Int>){
   fun doSomething(state:StateFlow<String>){
   ......
   }
}
j

Jakub Gwóźdź

10/09/2023, 9:16 AM
I wouldn’t pass a flow to be a member of a class, it looks like a recipe for having debatable fun. But passing to a method for collection or modification? I think it’s ok, but I’d refactor it to extension function for flow to be the recipient, e.g.:
Copy code
fun StateFlow<String>.doSomethingUsing(foo: Foo) { ... }
(after all, most of Flow API is exactly that - extension functions that do something on
this
flow and returns some result)
e

Eslam Hussein

10/09/2023, 10:33 AM
that what I'm expecting thanks 🙂
e

Erfannj En

10/09/2023, 11:09 AM
In my opinion, when it is necessary to encapsulate the logic of combining flows, why not?
c

Casey Brooks

10/09/2023, 6:33 PM
I’d say it’s probably fine to pass in a StateFlow, since it’s designed to have multiple collectors. Multiple parts of your app might need to observe the same State property, and passing around the StateFlow is a perfectly acceptable use-case. But passing a regular
Flow
could be problematic, since it will restart the entire flow for each Collector, but intuitively one might think the Flow is run only once and its values handled by multiple Collectors. Either pass in a SharedFlow to explicitly indicate a single Flow with multiple collectors, or pass in an object which can be used to create a new Flow, to make it explicit that each Collector is restarting the entire pipeline. For example:
Copy code
public interface FlowCreator {
    fun newFlow(): Flow<String>
}

class A {
    suspend fun doSomething(flowCreator: FlowCreator) {
        flowCreator.newFlow().collect { }
    }
}
The difference of using the Flow/StateFlow as the Receiver vs passing it into a class is mostly semantics. As a Receiver to a top-level function, it’s more commonly used for generic functionality which can be plugged into another pipeline. It’s an intermediate step of the Flow, or else it is a terminal operator but still needs to be run in some other coroutine which controls the lifecycle (for example, a ViewModel’s
viewModelScope
), but the operator itself it doesn’t really care exactly where it’s used. Passing it into a function/class implies that the Flow is being collected in there. The lifecycle of the Flow, then, is related to that particular class/function. For example, passing a Flow into a ViewModel implies that the Flow will be collected in that ViewModel’s
viewModelScope
.