Kotlin Flow for Android: Getting Started

In this tutorial, you’ll learn about the basics of Kotlin Flow, and you’ll build an Android app that fetches weather forecast data using Flow. By Dean Djermanović.

4.8 (35) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Kotlin Flow Basics

Flow is a stream that produces values asynchronously. Furthermore, Flow uses coroutines internally. And because of this, it enjoys all the perks of structured concurrency.

With structured concurrency, coroutines live for a limited amount of time. This time is connected to the CoroutineScope you start your coroutines in.

When you cancel the scope, you also release any running coroutines. The same rules apply to Kotlin Flow as well. When you cancel the scope, you also dispose of the Flow. You don’t have to free up memory manually! :]

There are some similarities between Kotlin Flow, LiveData and RxJava. All of them provide a way to implement the observer pattern in your code.

  • LiveData is a simple observable data holder. It’s best used to store UI state, such as lists of items. It’s easy to learn and work with. But it doesn’t provide much more than that .
  • RxJava is a very powerful tool for reactive streams. It has many features and a plethora of transformation operators. But it has a steep learning curve!
  • Flow falls somewhere in between LiveData and RxJava. It’s very powerful but also very easy to use! The Flow API even looks a lot like RxJava!

Both Kotlin Flow and RxJava are implementations of the Reactive Stream specification.

However, Flow uses coroutines internally and doesn’t have some of the features RxJava has. Partly because it doesn’t need some features, and partly because some features are still being developed!

Note: In Kotlin 1.3.0 release, core Flow APIs and basic operators are stable. APIs that are not stable have annotations @ExperimentalCoroutinesApi or @FlowPreview.

Now that you’ve had enough theory, it’s time to create your first Flow!

Flow Builders

Navigate to main.kt in the starter project.

You’ll start by creating a simple Flow. To create a Flow, you need to use a flow builder. You’ll start by using the most basic builder – flow { ... }. Add the following code above main():

val namesFlow = flow {
  val names = listOf("Jody", "Steve", "Lance", "Joe")
  for (name in names) {
    delay(100)
    emit(name)
  }
}

Make sure to add the imports from the kotlinx.coroutines package.

Here, you’re using flow() to create a Flow from a suspendable lambda block. Inside the block, you declare names and assigning it to a list of names.

Next, you used a for loop to go through the list of names and emit each name after a small delay. The Flow uses emit() send values to consumers.

There are other Flow builders that you can use for an easy Flow declaration. For example, you can use flowOf() to create a Flow from a fixed set of values:

val namesFlow = flowOf("Jody", "Steve", "Lance", "Joe")

Or you can convert various collections and sequences to a Flow:

val namesFlow = listOf("Jody", "Steve", "Lance", "Joe").asFlow()

Flow Operators

Moreover, you can use operators to transform Flows, as you would do with collections or sequences. There are two types of operators available inside the Flow – intermediate and terminal.

Intermediate Operators

Go back to main.kt and add the following code to main():

fun main() = runBlocking {
  namesFlow
      .map { name -> name.length }
      .filter { length -> length < 5 }
    
  println()
}

Here, you used the Flow of names from earlier and you applied two intermediate operators to it:

  • map transforms each value to another value. Here you transformed name values to their length.
  • filter selects values that meet a condition. Here you chose values that are less than five.

The important thing to notice here is the block of code inside each of these operators. These blocks of code can call suspending functions! So you can also delay within these blocks. Or you can call other suspending functions!

What happens with the Flow is visible on the image below:

Flow Filter Diagram

Flow will emit values one at a time. You then apply each operator to each of the values, once again, one at a time. And finally, when you start consuming values, you'll receive them in the same order.

Build and run by clicking the play button next to the main function.

You'll notice that nothing happens! This is because intermediate operators are cold. When you invoke an intermediate operation on a Flow, the operation is not executed immediately. Instead, you return the transformed Flow, which is still cold. The operations execute only when you invoke a terminal operator on the final stream.

Terminal Operators

Because Flows are cold, they won't produce values until a terminal operator is called. Terminal operators are suspending functions that start the collection of the flow. When you invoke a terminal operator, you invoke all the intermediate operators along with it:

An example of what would happen with original values, if you were to listen to a Flow:

Terminal Operation Diagram

As you start collecting values, you get one at a time, and you don't block while waiting for new values!

Now go back to the main.kt file and add the collect() terminal operator:

fun main() = runBlocking {
  namesFlow
      .map { name -> name.length }
      .filter { length -> length < 5 }
      .collect { println(it) }

  println()
}

Since collect() is a suspending function, it can only be called from a coroutine or another suspending function. This is why you wrap the code with runBlocking().

Build and run the code by clicking the play button. You'll get the following output:

4
3

collect() is the most basic terminal operator. It collects values from a Flow and executes an action with each item. In this case, you're printing an item to the console. There are other terminal operators available; you'll learn about them later in this tutorial.

You can check out the final code in the Playground-Final project.

Now that you know the basics of Flow, let's move to our weather app, where you'll see Kotlin Flow doing real work! :]

Flow on Android

Now you'll apply everything you've learned so far in an Android app! The Sunzoid app is a simple weather app that displays a forecast for a specific city. It fetches the weather data from the network and stores it into a database to support offline mode.

Open the Sunzoid-Starter project in Android Studio. Build and run the app, and you'll see an empty screen:

Sunzoid Initial Screen

There's a search icon in the top left corner. You can tap it to enter a specific location. If you do that now, nothing will happen. But hang on — you're going to implement this functionality next!

There's a fair amount of code in the starter project:

Sunzoid Project Structure

You'll focus on the use of Kotlin Flow in the app. But if you want, you can explore the code, and get familiar with the app!

The starter project follows Google's recommended guide to app architecture. You can find the guide on the Android developer site documentation:

Copyright 2020 Google LLC

Google Recommended Architecture

At the top of the scheme, there's a UI layer that talks to the ViewModel architecture component. ViewModel communicates with a data repository. The repository fetches the data from the network using Retrofit. It stores the data in a local Room database. Finally, it exposes the database data to the ViewModel.

Room and Retrofit, in their latest versions, support Kotlin Coroutines. The starter project is set up to use them with coroutines.

You'll use Kotlin Flow to pass the data from the database to the ViewModel. The ViewModel will then collect the data. You'll also use coroutines and Flow to implement the search functionality.