All You Need for MVI is Kotlin. How to Reduce Without a Reducer?
In this article, I describe my attempt to implement a simple state reducer based on Kotlin Flow.

Motivation and Context
Like any Android developer following the latest trends, I like MVI architecture and the unidirectional data flow concept. It solves many issues out of the box making our code even more bulletproof.
In this article, I won’t go into detail about what MVI is, but you can find many great write-ups about it. Like:
Playing with libraries like MVICore, Mobius, or Orbit inspired me to experiment and try to implement a flow that can perform state reduction.
That’s how StateReducerFlow was born. Let me explain how I’ve built it, how it works, and how you can use it.
Thinking Process
Please keep in mind that the following examples are simplified.
Let’s start with a simple counter. It has one state that can be changed with two events: decrement and increment.
Using the above approach, we can structure our logic in the following way:
Event -> ViewModel -> State
One issue, though, is that handleEvent
can be called from any thread. Having unstructured state updates can lead to tricky bugs and race conditions. Luckily, state.update()
is already thread-safe, but still, any other logic can be affected.
To solve that we can introduce a channel that will allow us to process events sequentially, no matter from which thread they come.
Much better. Now we process all events sequentially but state updates are still possible outside of the updateState
method.
Ideally, state updates should be only allowed during event processing.
To achieve that we can implement a simple reducer using runningFold
.
Now only the reduceState
method can perform state transformations.
When you look at this example ViewModel you may notice that only the reduceState
method contains important logic. Everything else is just boilerplate that needs to be repeated for every new ViewModel.
As we all like to stay DRY, I needed to find a way to extract the generic logic from the ViewModel.
That’s how StateReducerFlow was born.
StateReducerFlow
I wanted StateReducerFlow to be a StateFlow that can handle generic events. I started with this definition:
Moving forward I extracted my ViewModel logic to the new flow implementation:
As you can see, the only new things are a few overrides from StateFlow.
To construct the flow you provide the initial state, the function that can reduce it, and the coroutine scope in which the state can be shared.
The last missing part is a factory function that can create our new flow. I’ve decided to go with ViewModel
extension to access viewModelScope.
Now we can migrate our ViewModel to the new StateReducerFlow.
Voilà! The boilerplate is gone.
Now anyone who has access to StateReducerFlow
can send events to it, e.g.
That’s it! Are you interested in how it works in a real app or how it can be tested? See my example project:
https://github.com/linean/StateReducerFlow
Stay inspired!