In this tutorial you will learn about Flow and how to use in Kotlin android via simple step by step examples.

What is a Flow?

A flow is a type that can emit multiple values sequentially, as opposed to suspend functions that return only a single value.

For example,A good usage of a Flow for example is an app that needs to receive live updates from a database.

Flows are built on top of coroutines and can provide multiple values. A flow is conceptually a stream of data that can be computed asynchronously. The emitted values must be of the same type. For example, a Flow<Int> is a flow that emits integer values.

A flow is very similar to an Iterator that produces a sequence of values, but it uses suspend functions to produce and consume values asynchronously. This means, for example, that the flow can safely make a network request to produce the next value without blocking the main thread.

There are three entities involved in streams of data:

  • A producer produces data that is added to the stream. Thanks to coroutines, flows can also produce data asynchronously.
  • (Optional) Intermediaries can modify each value emitted into the stream or the stream itself.
  • A consumer consumes the values from the stream.

Let’s look at some examples.

Example 1: Simple Kotlin Android Flow Example

A simple example to get you started with Flows. This example will teach you:

  • Flow
  • Kotlin Coroutines
  • Viewbinding

Step 1: Create Project

Start by creating an empty Android Studio project.

Step 2: Dependencies

Add the following statements in your app/build.gradle:

    // coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'

    // coroutine lifecycle scopes
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"

Step 3: Enable ViewBinding and Java8

Next enable Java8 and ViewBinding in your android{} closure:

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    buildFeatures {
        viewBinding true
    }

Step 4: Layout

Nothing special in our layout:

activiy_main.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"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 5: Our MainActivity

Go to your MainActivity.kt and add the following imports:

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import xyz.teamgravity.flow.databinding.ActivityMainBinding

Extend the AppCompatActivity and declare our ActivityMainBinding object:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

In the onCreate() use the above ActivityMainBinding object to reference our layout:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

Now simulate getting work from the server as our background task:

        val flow = flow<Int> {
            for (i in 1..10) {
                emit(i)
                delay(1000L)
            }
        }

Then udpdate the UI:

        lifecycleScope.launch {
            // runs different coroutine from producer
            flow.buffer().filter {
                it % 2 == 0
            }.map {
                it * it
            }.collect {
                println("debug: $it")
                delay(2000L)
            }
        }
    }
}

Here’s the full code:

MainActivity.kt

package xyz.teamgravity.flow

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import xyz.teamgravity.flow.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // get data from imaginary server -> producer flow
        val flow = flow<Int> {
            for (i in 1..10) {
                emit(i)
                delay(1000L)
            }
        }

        // update ui when data ready -> consumer flow
        lifecycleScope.launch {
            // runs different coroutine from producer
            flow.buffer().filter {
                it % 2 == 0
            }.map {
                it * it
            }.collect {
                println("debug: $it")
                delay(2000L)
            }
        }
    }
}

Run

Copy the code or download it in the link below, build and run.

Reference

Here are the reference links:

Number Link
1. Download Example
2. Follow code author