Convert Flow to SharedFlow and StateFlow
Tags: android, kotlin, beginners, jetpackcompose
Published: 2023-01-20 23:00:00

**Explore different ways of converting Flow to SharedFlow and StateFlow using SharedFlow.emit(), StateFlow.value, Flow.ShareIn() and Flow.StateIn()**

This is part of the asynchronous flow series:

* [Part 1 - Exploring Android LiveData Usages and Behaviors](https://vtsen.hashnode.dev/exploring-android-livedata-usages-and-behaviors)
    
* [Part 2 - Introduction to Kotlin Flows and Channels](https://vtsen.hashnode.dev/introduction-to-kotlin-flows-and-channels)
    
* [Part 3 - Exploring Different Ways to Collect Kotlin Flow](https://vtsen.hashnode.dev/exploring-different-ways-to-collect-kotlin-flow)
    
* **Part 4 - Convert Flow to SharedFlow and StateFlow**

* [Part 5 - Flow, SharedFlow, StateFlow Class Diagram](https://vtsen.hashnode.dev/flow-sharedflow-stateflow-class-diagram)

- [Part 6 - Kotlin Flow - Combine, Merge and Zip](https://vtsen.hashnode.dev/kotlin-flow-combine-merge-and-zip)    
    
`Flow` is a cold stream. It emits value only when someone collects or subscribes to it. So it does NOT hold any data or state.

`SharedFlow` is a hot stream. It can emit value even if no one collects or subscribes to it. It does NOT hold any data too.

`StateFlow` is also a hot steam. It does NOT emit value, but it holds the value/data.

| Flow Type | Cold or Hot Stream | Data Holder |
| --- | --- | --- |
| Flow | Cold | No |
| SharedFlow | Hot (by default) | No |
| StateFlow | Hot (by default) | Yes |

> The reason why `SharedFlow` and `StateFlow` are hot streams by default, is they can also be cold streams depending on how you create them. See shareIn and stateIn sections below.

## What is data holder?

Data holder (can also be called as state holder) means it holds data. It retains and stores the last data of the stream, The data is also observable which allows subscribers to subscribe to it.

There are 3 types of data holders in Android development

* `LiveData`
    
* `StateFlow`
    
* `State` (Jetpack Compose)
    

3 of them are pretty similar, but they have differences. See the table below.

| Data Holder Type | Android or Kotlin Library? | Lifecycle Aware? | Required Initial Value? |
| --- | --- | --- | --- |
| LiveData | Android | Yes | No |
| StateFlow | Kotlin | No | Yes |
| State (Compose) | Android | No | Yes |

**StateFlow is Platform Independent**

`LiveData` is Android-specific and eventually will be replaced by `StateFlow`. Compose `state` is similar to `StateFlow` in my opinion. However, compose `State` is very specific to Android Jetpack Compose. So it is platform specific, whereas `StateFlow` is more generic and platform independent.

**StateFlow could be life-cycle aware**

`LiveData` itself is life-cycle aware and `StateFlow` is NOT. `StateFlow` could be life-cycle aware, depending on how you collect it. Compose `State` itself is NOT life-cycle aware. Since it is used by the composable functions, when a composable function leaves composition, it automatically unsubscribes from compose `State`.

**StateFlow requires Initial Value**

Creating `LiveData` does NOT require an initial value

```kotlin
// live data - data holder 
val liveData = MutableLiveData<Int>()
```

but, `StateFlow` and compose `State` require an initial value.

```kotlin
// state flow - data holder
val stateFlow = MutableStateFlow<Int?>(null)
// compose state - data holder
val composeState: MutableState<Int?> = mutableStateOf(null)
```

## Convert Flow to SharedFlow

The following example is based on this flow in your View Model class

```kotlin
val flow: Flow<Int> = flow {
    repeat(10000) { value ->
        delay(1000)
        emit(value)
    }
}
```

and this `sharedFlow` variable defined.

```kotlin
private var sharedFlow = MutableSharedFlow<Int>()
```

### 1\. Flow.collect() and SharedFlow.emit()

This converts the `Flow` to `SharedFlow` using `Flow<T>.collect` and manually call the `SharedFlow<T>.emit()`.

```kotlin
viewModelScope.launch { 
    flow.collect { value 
        -> sharedFlow.emit(value) 
    } 
}
```

This is a hot stream. So it emits the value regardless anyone collects it.

### 2\. Flow.shareIn()

You can also use `Flow<T>.shareIn()` to achieve the same result.

```kotlin
sharedFlow = flow.shareIn(
    scope = viewModelScope,
    started = SharingStarted.Eagerly
)
```

However, if you change `SharingStarted.Eagerly` to `SharingStarted.WhileSubscribed()`, the `SharedFlow` becomes a cold stream.

## Convert Flow to StateFlow

So we have this `stateFlow` variable defined in the view model.

```kotlin
private val stateFlow = MutableStateFlow<Int?>(null)
```

### 1\. Flow.collect() and StateFlow.value

This converts the `Flow` to `StateFlow`.

```kotlin
viewModelScope.launch { 
    flow.collect { value -> 
        stateFlow.value = value 
    } 
}
```

Similar to `SharedFlow`, this is a hot stream. The difference is `StateFlow` is a data holder and `SharedFlow` is not.

### 2\. Flow.stateIn()

You can also use `Flow<T>.stateIn()` to achieve the same result.

```kotlin
stateFlow = flow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.Eagerly,
    initialValue = null)
```

Similar to `Flow<T>.shareIn()` above, if you change `SharingStarted.Eagerly` to `SharingStarted.WhileSubscribed()`, the `StateFlow` becomes a cold stream.

In practice, it is advisable to use `SharingStarted.WhileSubscribed(5000)` instead of `SharingStarted.WhileSubscribed()` to account for screen rotation and prevent flow emission from restarting.

> **Important note:** If you call `Flow<T>.shareIn()` and `Flow<T>.stateIn()` multiple times, it creates multiple flows which emit the value in the background. This eventually causes unnecessary resource leaks that you want to prevent.

## Collect from SharedFlow and StateFlow

Collecting from `SharedFlow` and `StateFlow` is the same as collecting from the `Flow`. Refer to the following article on different ways of collecting flow.

- [Exploring Different Ways to Collect Kotlin Flow](https://vtsen.hashnode.dev/exploring-different-ways-to-collect-kotlin-flow) 

### 1\. Collect using RepeatOnLifecycle()

The recommended way to collect `Flow` by Google is using [`LifeCycle.RepeatOnLifecycle()`](https://vtsen.hashnode.dev/exploring-different-ways-to-collect-kotlin-flow#heading-3-collect-flow-using-lifecyclerepeatonlifecycle). So, we're going to use it as an example.

This example below converts the `stateFlow` to compose state, which is the data holder for composable function.

```kotlin
@Composable 
fun SharedStateFlowScreen() { 
    val viewModel: StateSharedFlowViewModel = viewModel() 
    val lifeCycle = LocalLifecycleOwner.current.lifecycle

    /* compose state - data holder */ 
    var composeStateValue by remember { mutableStateOf<Int?>(null) }

    /* collect state flow and convert the data to compose state */     
    LaunchedEffect(true) { 
        lifeCycle.repeatOnLifecycle(state = Lifecycle.State.STARTED) {
            viewModel.stateFlow.collect { composeStateValue = it } 
        } 
    } 
}
```

### 2\. Collect using collectAsStateWithLifecycle()

If you use Android lifecycle Version 2.6.0-alpha01 or later, you can reduce the code significantly using `Flow<T>.collectAsStateWithLifecycle()` API

First, you need to have this dependency in your `build.gradle` file.

```kotlin
dependencies { 
    implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha02' 
}
```

Then, you can reduce the code to the following. Please note that you need to specify the `@OptIn(ExperimentalLifecycleComposeApi::class)`

```kotlin
@OptIn(ExperimentalLifecycleComposeApi::class) 
@Composable 
fun SharedStateFlowScreen() { 
    val viewModel: StateSharedFlowViewModel = viewModel()

    /* compose state - data holder */ 
    val composeStateValue by   
        viewModel.stateFlow.collectAsStateWithLifecycle() 
}
```

## Best Practices?

Honestly, what I find difficult in Android development is there are just way too many options to accomplish the same thing. So which one we should use? Now, we have `LiveData`, `Flow`, `Channel`, `SharedFlow`, `StateFlow` and compose `State`. In what scenario, we should use which one?

So I document the best practices based on various sources and my interpretations. I do not know whether they make sense. Things like this are likely very subjective too.

1. **Do NOT use LiveData** especially if you're working on a new project. `LiveData` is legacy and eventually will be replaced by `StateFlow`.
    
2. **Do NOT expose Flow directly in View Model**, convert it to `StateFlow` instead. This can avoid unnecessary workload on the main UI thread. `Flow` is a cold stream, it emits data (or restarts the data emission) every time you collect it.
    
3. **Expose StateFlow in your view model** instead of compose `State`. Since `StateFlow` is platform independent, it makes your view model platform independent which allows you easy migration to KMM (which allows you to target both IOS and Android) for example. `StateFlow` is also more powerful (e.g. it allows you to combine multiple flows into one etc.)
    
4. **Collect StateFlow in your UI elements** (either in activity or composable function) and convert the data to compose `State`. Compose `State` should be created and used only within the composable functions.
    

> Whether ViewModel should hold `StateFlow` or compose `State` is questionable. I have been using compose `State` but it seems like `StateFlow` might be a better option here based on more complex use cases such as combining flow?

> On the other hand, if I convert `Flow` to compose `State` in ViewModel directly, I don't need to convert it again in the composable function. Why do I need 2 state/data holders and collect twice?

**What about one-time event?**

I do not sure of the use case of `SharedFlow` and `Channel`. It appears to be used as a one-time event. If you have one subscriber, you use `Channel`. If you have multiple subscribers, you use `SharedFlow`.

However, this article here by the Google team kind of imply using `SharedFlow` or `Channel` as a one-time event is not recommended. It is mainly because they're hot stream, which runs into a risk of missing the events when the app is in the background or during configuration.

It seems to me we should probably just **use StateFlow for everything**. Let's forget the rest! Maybe this is easier this way...

## Source Code

GitHub Repository: [Demo\_AsyncFlow](https://github.com/vinchamp77/Demo_AsyncFlow) (see the `SharedStateFlowActivity`)

---

*Originally published at [https://vtsen.hashnode.dev](https://vtsen.hashnode.dev/convert-flow-to-sharedflow-and-stateflow)*.