Kotlin Flow in Android with Example
Last Updated :
25 May, 2025
Kotlin Flow is a tool that helps developers work with data that changes over time like search results, live updates, or user input. It’s part of Kotlin’s coroutines, which are used for writing code that doesn’t block the app while waiting for something to finish, like a network call or a file to load. Flow gives you a simple and clean way to deal with streams of data that comes in bit by bit instead of all at once.
What is exactly Kotlin Flow?
It is a way to handle data that comes in slowly or over time like lazy flow. Instead of using callbacks, it gives you a better way to work with this kind of data using coroutines. A Flow doesn’t start doing anything until you ask it to. For example, if you have a Flow that gets data from the internet, it won’t start downloading until you actually start using it.
What are we going to build in this article?
We will build a simple app that fetches some data from API and shows it on the screen. It's a simple app demonstrating the Kotlin flow working. It will use MVVM architecture.
Prerequisites:
- Good knowledge of Android
- Knowledge of Kotlin
- Basics of MVVM Architecture
- Basics of Retrofit Library
- Basics of Kotlin coroutines
- Basics of View Binding
Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio.
Note that select Kotlin as the programming language.
Project structure
Step 2: Add required dependencies
Navigate to the Gradle Scripts > build.gradle(Module:app) and add the below dependency in the dependencies section for Retrofit2 and Gson Converter for Api Calls.
dependencies {
...
implementation ("com.google.code.gson:gson:2.9.1")
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
}
Step 3: Allow Internet Permission in Manifest
Navigate to app > manifests > AndroidManifest.xml and add the following line of code to provide internet permission for api calls.
<uses-permission android:name="android.permission.INTERNET" />
Step 4: Working with the activity_main.xml
Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Below is the code for the activity_main.xml file. This file will contain a editText to enter the id as a query and display data fetched from api in the textviews.
activity_main.xml:
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:background="@color/white"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<com.google.android.material.divider.MaterialDivider
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="-16dp"
app:layout_constraintBottom_toBottomOf="@+id/comment_textview"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/search_edit_text" />
<EditText
android:id="@+id/search_edit_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:autofillHints="Search Comments By Id"
android:inputType="number"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/button"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Search"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:layout_width="match_parent"
android:id="@+id/progress_bar"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_edit_text" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="20dp"
android:text="Id"
android:textAlignment="viewEnd"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_edit_text" />
<TextView
android:id="@+id/comment_id_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/textView" />
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:textAlignment="viewEnd"
android:text="Name"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/comment_id_textview" />
<TextView
android:id="@+id/name_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/textView2" />
<TextView
android:id="@+id/textView3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:textAlignment="viewEnd"
android:text="Email"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/name_textview" />
<TextView
android:id="@+id/email_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/textView3" />
<TextView
android:id="@+id/textView4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:text="Comment"
android:textAlignment="viewEnd"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/email_textview" />
<TextView
android:id="@+id/comment_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/textView4" />
</androidx.constraintlayout.widget.ConstraintLayout>
Design UI:
Step 5: Create a model to save data from API calls
We will be using https://jsonplaceholder.typicode.com/comments API, It gives some JSON data when id is passed as a path. For example, https://jsonplaceholder.typicode.com/comments/2 gives JSON which contains some random data. We will be using this data and show it on the screen using kotlin flow. Create a kotlin file named CommentModel and create a dataclass to parse data that is received from the API.
Example ResponseWe need to create a dataclass for this response. Add the following code in CommentModel
CommentModel.kt:
Kotlin
package org.geeksforgeeks.demo
import com.google.gson.annotations.SerializedName
data class CommentModel(
val postId: Int?=null,
val id: Int?=null,
val email: String?=null,
val name:String?=null,
@SerializedName("body")
val comment: String?=null
)
Step 6: Create an API Service
We need to create an API interface to call the API using a retrofit. Create a kotlin file named ApiService.kt and add the following code
ApiService.kt:
Kotlin
package org.geeksforgeeks.demo
import retrofit2.http.GET
import retrofit2.http.Path
interface ApiService {
// Get method to call the api ,passing id as a path
@GET("/comments/{id}")
suspend fun getComments(@Path("id") id: Int): CommentModel
}
Step 7: Create a helper class from saving States of API calls
Let's add some helper classes to handle the loading or error state of API. Create a kotlin file named CommentApiState.kt. Refer to the comment in the code for an explanation.
CommentApiState.kt:
Kotlin
package org.geeksforgeeks.demo
data class CommentApiState<out T> (
val status: Status,
val data: T?,
val message: String?
) {
companion object {
// In case of Success,set status as
// Success and data as the response
fun <T> success(data: T?): CommentApiState<T> {
return CommentApiState(Status.SUCCESS, data, null)
}
// In case of failure ,set state to Error ,
// add the error message,set data to null
fun <T> error(msg: String): CommentApiState<T> {
return CommentApiState(Status.ERROR, null, msg)
}
// When the call is loading set the state
// as Loading and rest as null
fun <T> loading(): CommentApiState<T> {
return CommentApiState(Status.LOADING, null, null)
}
}
}
// An enum to store the
// current state of api call
enum class Status {
SUCCESS,
ERROR,
LOADING
}
Step 8: Create a Retrofit Instance for network calls
Create a kotlin file named RetrofitInstance.kt and add code to create API service, which will be used to make API calls.
RetrofitInstance.kt:
Kotlin
package org.geeksforgeeks.demo
import com.google.gson.GsonBuilder
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitInstance {
// Base url of the api
private const val BASE_URL = "https://jsonplaceholder.typicode.com/"
// create retrofit service
fun apiService(): ApiService =
Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
.create(ApiService::class.java)
}
Step 9: Create a Repository
Create a kotlin file with the name CommentsRepository.kt. Add the following code. Refer to the comments for an explanation. This repository calls the ApiService and returns the data as a flow in the IO Thread.
CommentsRepository.kt:
Kotlin
package org.geeksforgeeks.demo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
class CommentsRepository(private val apiService: ApiService) {
fun getComment(id: Int): Flow<CommentApiState<CommentModel>> {
return flow {
// get the comment Data from the api
val comment= apiService.getComments(id)
// Emit this data wrapped in
// the helper class [CommentApiState]
emit(CommentApiState.success(comment))
}.flowOn(Dispatchers.IO)
}
}
Step 10: Working with the ViewModel
Create a kotlin class file named CommentViewModel.kt .Add the following code. Refer to the comments for an explanation.
CommentViewModel.kt:
Kotlin
package org.geeksforgeeks.demo
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch
class CommentsViewModel : ViewModel() {
// initialize the repo and pass the api service as parameter
private val repository = CommentsRepository(
RetrofitInstance.apiService()
)
// MutableStateFlow to store comment data
val commentState = MutableStateFlow(
CommentApiState(
Status.LOADING,
CommentModel(), ""
)
)
init {
// Initiate a starting search with comment Id 1
getNewComment(1)
}
// Function to get new Comments
fun getNewComment(id: Int) {
// initialize state as loading
commentState.value = CommentApiState.loading()
// use coroutine scope to let the api call run asynchronously
viewModelScope.launch {
// Collecting the data emitted by the function in repository
repository.getComment(id)
// handle errors like 404 not found, invalid id etc
.catch {
commentState.value =
CommentApiState.error(it.message.toString())
}
// on the success response, data is set to state for observing and updating
.collect {
commentState.value = CommentApiState.success(it.data)
}
}
}
}
Step 11: Working with MainActivity.kt
Open presentation > MainActivity.kt. Add the following code, refer to the comments for explanation. We now need to call the API from view(MainActivity) and show the data on the screen.
MainActivity.kt:
Kotlin
package org.geeksforgeeks.demo
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import org.geeksforgeeks.demo.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
// initiate the viewmodel
private val viewModel: CommentsViewModel by lazy {
ViewModelProvider(this)[CommentsViewModel::class.java]
}
// create a view binding variable
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// set on click listener for search button
binding.button.setOnClickListener {
val id = binding.searchEditText.text.toString().trim()
// check to prevent api call with no parameters
if (id.isEmpty() || id == "0") {
Toast.makeText(this, "Id Can't be 0 or empty", Toast.LENGTH_SHORT).show()
} else {
// if id isn't empty, make the api call
viewModel.getNewComment(id.toInt())
}
}
// Since flow run asynchronously, start listening on background thread
lifecycleScope.launch {
viewModel.commentState.collect {
// When state to check the state of received data
when (it.status) {
// on loading, show the progress bar
Status.LOADING -> {
binding.progressBar.isVisible = true
}
// on success, update ui and make progress bar invisible
Status.SUCCESS -> {
binding.progressBar.isVisible = false
// handle null pointer exception
it.data?.let { comment ->
binding.commentIdTextview.text = comment.id.toString()
binding.nameTextview.text = comment.name
binding.emailTextview.text = comment.email
binding.commentTextview.text = comment.comment
}
}
// on error, show toast with error message
else -> {
binding.progressBar.isVisible = false
Toast.makeText(this@MainActivity, "${it.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
}
}
Output:
Now run the app, put some numbers and click search. Enter any number from 1-500, It will return a successful state.
Similar Reads
OpenIntents in Android with Example
OI refers to the "OpenIntents" project in Android are a way for one app to request an action from another app. This can be done using either explicit or implicit intents, allowing them to share functionality. The OpenIntents project provides a set of commonly-used intents that can be used by develop
4 min read
Synchronization in Android with Example
In Android, synchronization refers to the process of ensuring that data stored in multiple locations is the same and up-to-date. This can be achieved through various methods such as using the built-in Android synchronization adapters, or by manually implementing synchronization using the Android Syn
5 min read
Line Graph View in Android with Example
If you are looking for a view to represent some statistical data or looking for a UI for displaying a graph in your app then in this article we will take a look on creating a line graph view in our Android App using the GraphView library. What we are going to build in this article? We will be buildi
3 min read
GridView in Android with Example
A GridView is a type of AdapterView that displays items in a two-dimensional scrolling grid. Items are inserted into this grid layout from a database or from an array. The adapter is used for displaying this data, setAdapter() method is used to join the adapter with GridView. The main function of th
5 min read
Android Sensors with Example
In our childhood, we all have played many android games like Moto Racing and Temple run in which by tilting the phone the position of the character changes. So, all these happen because of the sensors present in your Android device. Most Android-powered devices have built-in sensors that measure mot
4 min read
Android Fade In/Out in Kotlin
In Android Animations are the visuals that are added to make the user interface more interactive, clear and good looking. Fade In and Fade out animations are used to modify the appearance of any view over a set interval of time so that user can be notified about the changes that are occurring in our
3 min read
Calendar View App in Android with Kotlin
Calendar View is seen in most travel booking applications in which the user has to select the date of the journey. For the selection of the date, this view is used. In this article, we will take a look at How to implement Calendar View within our Android application using Kotlin. A sample video is g
3 min read
Jetpack LiveData in Android with Example
Android Jetpack is a suite of libraries to help developers follow best practices, reduce boilerplate code, and write code that works consistently across Android versions and devices so that developers can focus on the code they care about. Here, we are going to implement Jetpack Live Data in Android
4 min read
Android ListView in Java with Example
A ListView in Android is a type of AdapterView that displays a vertically scrollable list of items, with each item positioned one below the other. Using an adapter, items are inserted into the list from an array or database efficiently. For displaying the items in the list method setAdaptor() is use
3 min read
Popup Menu in Android With Example
In Android development, Menus are an important part of the user interface, providing users with easy access to common functionalities and ensuring a smooth and consistent experience throughout the application. In Android, we have three types of Menus available to define a set of options and actions
4 min read