safeApiCall is an extension function I created that handles HTTP Exceptions from the API and wraps the result in a ResultWrapper. If the API request is successful, we save it into the database and then return wrapped Result
at the end.
dog?.run { dogDao.save(this) }
Where dogDao
is an interface with a suspend function save
Now call the above function from the ViewModel:
And now from the MainActivity.kt, set up an On Click listener which triggers the request and also observe the fetchedLiveData
for results. If you notice carefully I only use this to show the status of the request(Loading, No internet Connection) and not the fetched data.
Setting up Flows
In the DogDao.kt interface define this function:
@Query("SELECT * FROM dog") fun loadAllDogsFlow(): Flow<List<Dog>>
Room 2.2 has out of the box support for flows. The above method will return a flow of lists of dogs. When the Load more button is pressed in the view, a new dog is added to the database and since the list of the dogs in the database is dynamic (stream), a flow is a perfect choice for this.
In another words we need to use flow in this case because the list of dogs in the database will keep updating.
Now call this method from MainActivityRepository.kt:
val dogListFlow = dogDao.loadAllDogsFlow()
Now in the MainViewmodel.kt, we need to convert this flow into Live data which we’ll then observe from the view:
asLiveData()
is an extension function that collects the flow and emits it as live data.
Now finally in the MainActivity.kt, observe the above live data:
Since this guide is specifically for learning flows, I am not adding code for adapters or layout. You can find the entire code at GitHub.
At this point you have have a working app that displays data from database (which is Single Source of Truth) using flows which can be updated by executing a one shot request by pressing the “Load More” button in the view.
Combining Flows
Now in RemoteDataSource.kt let’s set up another flow that emits a list of top (random actually) 20 dog breeds every two seconds(in a real life use case scenario this could be a network request):
Now in MainActivityRepositoty.kt, combine the above flow with the flow of list of dogs from the database using combine
operator:
Combine(){}
returns a flow whose values are generated with transform function by combining the most recently emitted values by each flow.- While
flowOn(context: CoroutineContext)
changes the context where the flow is to be executed and affects only preceding operators that do not have it’s own context. This basically means the functionapplyTopDogsToDatabaseList
extension function runs in theDispatchers.Default
thread because we used.flowOn(Dispatchers.Default)
. .conflate()
makes sure that emitter is never suspended due to a slow collector, but collector always gets the most recent value emitted.
And That’s it. ✅
You have now built an app that fetches a dog breed using a one shot request, saves it to the database and then displays the list of dogs in database using flows, combines it with another flow that emits every two seconds and then displays the list of dogs in the MainActivity.
The Top Dogs (which keeps changing every two seconds) are represented by pink background in the below GIF.
Shivamdhuria/flows_guideContribute to Shivamdhuria/flows_guide development by creating an account on GitHub. github.com |
![]() |
In Part 2 of this guide, We will implement a search bar to query the dog breeds using Channels and Flows. (Link Here)