polpetta
0.0.8indexedA library for managing state in applications using the MVI pattern, enabling state mutation through DSL-defined actions, supporting side effects, and offering customizable state transformation.
A library for managing state in applications using the MVI pattern, enabling state mutation through DSL-defined actions, supporting side effects, and offering customizable state transformation.

It's "another" MVI library but with a funny name. With this library you will be able to mutate your State by using a Action defined by using DSL.
repositories {
mavenCentral()
}
dependencies {
implementation 'dev.mcatta:polpetta:0.0.8'
}
The full reference documentation is available here.
You application's State must extend State and your action the Action class. Every Action can prompt a Reducer which basically manipulate your State.
This is you State and Actions definition, now you need to write a StateStore which basically will persist your state and your Action/Reducer definition.
class CounterStore(scope: CoroutineScope) : StateStore<CounterAction, CounterState, MySideEffect>(
coroutineScope = scope,
initialState = CounterState.Count(0),
reducerFactory = {
on<CounterAction.Decrease, CounterState.Count> { action, state ->
state.mutate { copy(counter = counter - 1) }
}
on<CounterAction.Increase, CounterState.Count> { action, state ->
state.mutate { copy(counter = counter + 1) }
}
on<CounterAction.Set, CounterState.Count> { action, state ->
state.mutate { copy(counter = action.n) }
}
on<CounterAction.DoNothing> { action, state ->
state.nothing()
}
on<CounterAction.ToString, CounterState.Count> { action, state ->
state.transform<CounterState.Result> { CounterState.Result(counter.toString()) }
}
// ...
}
)
You can add the debugMode = true to enable the logging.
StateStore<CounterAction, CounterState, MySideEffect>(
coroutineScope = scope,
debugMode = true,
initialState = CounterState.Count(0),
reducerFactory = {}
)
The reducer supports three types of operations:
{ action, state -> state.nothing() }
which basically doesn't change the state
{ action, state -> state.mutate { copy(counter = counter + 1) } }
which mutate the properties of the current state (Note: your state must be data class in order to copy it)
{ action, state -> state.transform { CounterState.Result(counter.toString()) } }
which allows to change the current state into a new one of different type
Polpetta supports also side effects. In order to support that we need to specify which SideEffect class we want to use:
class CounterStore(scope: CoroutineScope) : StateStore<CounterAction, CounterState, MySideEffect>
otherwise we can say Nothing
class CounterStore(scope: CoroutineScope) : StateStore<CounterAction, CounterState, Nothing>
Then given a specific SideEffect event we can prompt it inside the Reducer's scope, like this:
// Inside the StateStore
on<TestAction.Increase, TestState.Count> { _, stateModifier ->
sideEffect(TestSideEffect.Toast("Show message"))
stateModifier.mutate<TestState.Count> { copy(counter + 1) }
}
// On the View
testStore.sideEffectFlow.collect {}
Copyright 2025 Marco Cattaneo
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance the License.
You may obtain a copy of the License at
https:
Unless applicable law agreed to writing, software
distributed under the License distributed an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
See the License the specific language governing permissions
limitations under the License.
sealed interface CounterState : State {
data class Count(val counter: Int) : TestState
data class Result(val message: String) : TestState
}
sealed interface CounterAction : Action {
object Increase : CounterAction
object Decrease : CounterAction
data class Set(val n: Int) : CounterAction
object DoNothing : CounterAction
object ToString : CounterAction
}
interface MySideEffect : SideEffect
Surfaced from shared tags and platforms — no rankings paid for.