Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to modify Kotlin Coroutine Flow value after initialization?

Expected

Initialize a Kotlin Coroutine Flow and modify its' value after the Flow's creation.

The desired functionality is similar to MutableLiveData's setValue which allows data to be added to an existing MutableLiveData object. In the example below setValue is called on the _feed MutableLiveData value in FeedViewModel.kt in order to update the value.

This works as expected, emitting the values in FeedFragment.kt

FeedViewState.kt

data class _FeedViewState(
    val _feed: MutableLiveData<List<Tweet>> = MutableLiveData()
)

data class FeedViewState(private val _feedViewState: _FeedViewState) {
    val feed: LiveData<List<Tweet>> = _feedViewState._feed
}

FeedViewModel.kt

class FeedViewModel(...) : ViewModel() {
    private val _feedViewState = _FeedViewState()
    val feedViewState = FeedViewState(_feedViewState)
    init {
        viewModelScope.launch(Dispatchers.IO) {
            feedRepository.getFeed().collect { results ->
                when (results.status) {
                    LOADING -> ...
                    SUCCESS -> withContext(Dispatchers.Main) {
                        _feedViewState._feed.value = results.data
                    }
                    ERROR -> ...
                }
            }
        }
    }
}

FeedFragment.kt

class FeedFragment : Fragment() {
    override fun onCreateView(...): View? {
        viewModel.feedViewState.feed.observe(viewLifecycleOwner){ feed ->
                //Do something with the tweets here.
        }        
        return inflater.inflate(R.layout.fragment_feed, container, false)
    }
}

Observed

In order to implement the same pattern using Kotlin Coroutines the LiveData in the FeedViewState.kt is replaced with Flow. In FeedViewModel.kt the desired result is to add data to the _feed Flow value. The attempted solutions have been applying map, and emit inside of transform and onCompletion to the _feed Flow value.

However, this solution does not emit the desired values from the Flow value in FeedFragment.kt.

FeedViewState.kt

data class _FeedViewState(
    val _feed: Flow<List<Tweet>> = flow { }
)

data class FeedViewState(private val _feedViewState: _FeedViewState) {
    @ExperimentalCoroutinesApi
    val feed: Flow<List<Tweet>> = _feedViewState._feed }
}

FeedViewModel.kt

class FeedViewModel(...) : ViewModel() {
    private val _feedViewState = _FeedViewState()
    val feedViewState = FeedViewState(_feedViewState)
    init {
        viewModelScope.launch(Dispatchers.IO) {
            feedRepository.getFeed().collect { results ->
                when (results.status) {
                    LOADING -> ...
                    SUCCESS -> withContext(Dispatchers.Main) {
                        _feedViewState._feed.map { results.data!! }
                    }
                    ERROR -> ...
                }
            }
        }
    }
}

FeedFragment.kt

class FeedFragment : Fragment() {
    override fun onCreateView(...): View? {
        lifecycleScope.launch {
            viewModel.feedViewState.feed.collect { tweets ->
                //Do something with the tweets here.
            }
        }
        return inflater.inflate(R.layout.fragment_feed, container, false)
    }
}
like image 486
Adam Hurwitz Avatar asked Mar 10 '20 18:03

Adam Hurwitz


People also ask

How do you emit value in flow Kotlin?

To create flows, use the flow builder APIs. The flow builder function creates a new flow where you can manually emit new values into the stream of data using the emit function. In the following example, a data source fetches the latest news automatically at a fixed interval.

Why use flow instead of LiveData?

StateFlow and LiveData have similarities. Both are observable data holder classes, and both follow a similar pattern when used in your app architecture. The StateFlow and LiveData do behave differently: StateFlow requires an initial state to be passed into the constructor, while LiveData does not.

Is Kotlin flow lifecycle aware?

Stay organized with collections Save and categorize content based on your preferences. Kotlin coroutines provide an API that enables you to write asynchronous code.

What is buffer in Kotlin flow?

A container for data of a specific primitive type. A buffer is a linear, finite sequence of elements of a specific primitive type.


2 Answers

Kotlin StateFlow is a newer solution compared to Kotlin Channels that allow mutable updates to the Flow value as well as the ability to immutable observe data streams.

like image 162
Adam Hurwitz Avatar answered Oct 16 '22 09:10

Adam Hurwitz


Flow is meant to be self contained. What I would suggest is using ConflatedBroadcastChannel which offers functionality I believe you are looking for but this is currently an experimental feature in kotlin

like image 1
tyczj Avatar answered Oct 16 '22 10:10

tyczj