1. Introduction
Kotlin’s Flow API is a powerful tool for handling asynchronous data streams. It allows developers to work with streams of data in a reactive manner, making it easier to manage updates and changes in state.
A common requirement when working with flows is to access the previous value emitted by the flow while processing the current one.
In this tutorial, we’ll explore various techniques to achieve this.
2. Manually Tracking the Previous Value
The first approach involves manually keeping track of the flow’s previous value while processing the current one. This gives us full control over the state between emissions, ensuring that the previous value is readily available whenever a new value is emitted:
@Test
fun `test manually tracking previous and current value`() = runTest {
val flow = flowOf(1, 2, 3, 4, 5)
var previousValue: Int? = null
val results = mutableListOf<Pair<Int?, Int>>()
flow.collect { currentValue ->
results.add(Pair(previousValue, currentValue))
previousValue = currentValue
}
assertEquals(listOf(Pair(null, 1), Pair(1, 2), Pair(2, 3), Pair(3, 4), Pair(4, 5)),results)
}
In this example, we initialize a previousValue variable to null. As the flow emits new values, we collect the current and previous values by storing them as pairs in the results list. After each emission, we update previousValue from currentValue.
3. Using the runningFold() Method
The runningFold() method provides a more convenient way to maintain state across flow emissions. While manually tracking the previous value gives us flexibility, runningFold() handles this more cleanly by accumulating a result throughout emissions.
This method allows us to easily track both the previous and current values emitted by the flow, and it’s particularly useful when we need to transform or accumulate state at each step.
@Test
fun `test runningFold for previous and current value`() = runTest {
val flow = flowOf(1, 2, 3, 4, 5)
val initial: Pair<Int?, Int?> = null to null
val results = mutableListOf<Pair<Int?, Int?>>()
flow.runningFold(initial) { lastPair, next ->
lastPair.run {
val (_, last) = this
last to next
}
}
.collect { results.add(it) }
assertEquals(
listOf(Pair(null, null), Pair(null, 1), Pair(1, 2), Pair(2, 3), Pair(3, 4), Pair(4, 5)),
results
)
}
In this example, the runningFold() method creates a stateful pair at each step of the flow, starting with an initial pair of null to null to represent no previous value. With each emission, the lastPair is updated by pairing the previous value (last) with the new value (next). This approach cleanly tracks both the previous and current values. As a result, it provides a simple and efficient way to monitor value changes across the flow’s emissions.
4. Conclusion
In this article, we’ve explored two ways to access the current and previous values in a Kotlin Flow. Manually tracking the previous value offers flexibility and control, allowing us to handle the state between emissions. On the other hand, the runningFold() method provides a more functional and concise approach to maintaining state across flow emissions.