The purpose of this blog is to educate readers about the Kotlin Flow API. With Kotlin’s extensive set of built-in capabilities, we can easily do a wide range of jobs for our project.
The Flow API in Kotlin
First, we’ll look into the Kotlin Flow API. Flow is a kind of asynchronous data stream that often originates from a task and may end with or without exceptions. It sends values to the collector.
After we go through the example, this will all make more sense. Consider the common practice of picture downloading as an example.
You may download a picture by emitting the items (values) that represent the download percentage, such as 1%, 2%, 3%, etc. Any number of exceptions won’t stop you from finishing it. If everything proceeds as planned, we will successfully complete the task. Nevertheless, we shall finish the operation with an exception in the event of a network failure. As a result, the collector will receive values emitted by tasks.
Key elements of Flow
This is a list of the main parts of Flow:
- Flow Builder
- Operator
- Collector
Flow Builder
To put it simply, it aids in the execution of a job and the emission of objects. At times, it’s sufficient to emit a little number, or only a few pieces (1, 2, 3). The flow builder is useful here for this purpose. This might serve as a speaker in our minds. The speaker will reason (perform an action) and communicate (emit data).
Operator
Assisting with data format conversion is the operator’s job. A good analogy for the operator would be a translator. Assume that the collector is limited to understanding spoken English, while the speaker is communicating in French. Thus, a French-to-English translation is required. That translator is an operator.
Not only that, but operators also allow us to specify the thread that will execute the action.
Collector
The operators change the objects that the Flow Builder gathers from the emitted stream. We may consider a collector to be a listener. Actually, the collector is a kind of operator, often called a terminal operator. One who operates terminals is the collector. We will not cover the terminal operator in this Flow API blog since it is not essential.
The StateFlow
The purpose of creating a StateFlow is to hold and emit the present, observable, and updateable value’s state. The moment the collector begins collecting, it begins to emit values. Now is the time to use StateFlow. What this implies is that it continuously updates all collectors with its most recent value and has an initial value. For that reason, it never stores anything other than the most recent value. The most recent value is automatically applied to newly started collectors. It’s not possible for StateFlow to emit values in a sequential fashion.
It holds off on emitting the value till it’s different from the preceding one. Except for the Android component’s lifecycle awareness, it’s similar to LiveData. If we combine StateFlow with the repeatOnLifecycle scope, we can make it lifetime aware and make it identical to LiveData. If your user interface has to respond to changes in the state of a component (like a viewModel), StateFlow is the way to go.
For use cases such as UI state in Android applications, it is crucial that it remembers the latest transmitted state. In addition, StateFlow is thread-safe. It works just fine when shared across many threads.
The SharedFlow
If no collector is available when an event is emitted, SharedFlow may handle the stream of data such that several collectors can see them at once without missing any events. It works well in situations when you need to share the data stream. It is a hot flow.
With a buffer specified, SharedFlow may emit many values, but it does not store the last state. Interestingly, you can regulate the frequency at which new collectors view previous events.
Cold Flows
The collection process is the only way for cold flow to produce or emit values. Put another way, cold flows aren’t very active. That is, unless a consumer begins collecting them, they do nothing and do not run any code. It doesn’t keep any records. This indicates that the values are consumed during flow collection and cannot be kept for subsequent collectors.
Each collector will start at the beginning of the whole flow. We can compare it to 1-to-1 mapping. This rules out the possibility of using several collectors in a cold flow. Every collector will experience a new flow as a result.
Hot Flows
Hot Flows are constantly active and do not rely on collectors to start. The presence or absence of collectors does not affect the values it emits. They continue to release values even when no one is actively collecting them. “Broadcasts” is a good analogy.
Furthermore, collectors obtain their values from the point when they began collecting, and hot flow will continue to emit the values. Hot Flows are similar to mapping from 1 to N. For N collectors, it indicates a single flow. What this implies is that a hot flow might have several collectors, and that different collectors, based on their join timing, can obtain different updates or live events.
Flow API Source Code
Here is how the Flow interfaces appear in the Coroutines code:

Hello World of Flow
Now we may review the code.
- An initial flow builder is outputting integers from 0 to 10.
- After that, there’s a map operator that will square all of the values (it * it). The map is an Intermediate Operator map.
- Afterwards, we have a collector that displays the output values as follows: 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100.
Just so you know, it won’t start running until we link the flow builder and collector using the collect function.
We should investigate the Flow Builder more at this point.
Types of flow builders
The four main categories of flow builders are:
- Create a flow from a specified collection of objects using the flowOf() function.
- The asFlow() extension method facilitates the transformation of types into flows.
- What we have used in the Hello World example of Flow is flow{}•.
- ChannelFlow{}• This builder uses the send function that it provides to construct flow with the components.
Examples:
flowOf()
asFlow()
flow{}
channelFlow{}
flowOn Operator
To control the thread that will execute the job, the flowOn operator is a lifesaver. In Android, it is common practice to do an operation on a background thread and then display the outcome on the UI thread.
Here’s an example to help illustrate: In order to mimic delay, we have included a 500 ms delay within the flow builder.
In this case, the background thread will execute the flow builder’s internal tasks.Default dispatcher
The next step is to move focus to the user interface thread. To achieve this, we need to integrate our collection API into the launch process.Main Dispatchers
Using the flowOn operator in this way allows one to control the thread.
flowOn()is like subscribeOn() in RxJava
Dispatchers
They are useful for determining which thread is required for the task at hand. The most common kinds of dispatchers are Main, Default, and IO. When dealing with disks and networks, an IO dispatcher is the tool to employ. Tasks that need a lot of CPU power utilize the default. The main thread is responsible for the user interface of Android.
We will now go over the steps necessary to use the Flow builder to develop our flow. Using the Flow Builder, we can build a Flow for anything.
Creating Flow Using Flow Builder
We can learn it by looking at examples.
Step 1: Transfer the file to a new folder
Here, we will utilize the Flow Builder to construct our flow, which will transfer the file to the background thread and subsequently update the main thread on its progress.
2. Image DownloadingTo download the image, we’ll use the Flow Builder to build a flow that runs in the background while the main thread updates the collector on the download’s progress.
In this way, we may establish our Flow.
The Flow API allows Coroutine, which is essentially RxJava’s scheduler in Kotlin, to function similarly to its Android counterpart.