
Lightweight Redux/MVI-like library offering a simple, type-safe, coroutine-based API. Features include lifecycle-safe flow collection, ViewModel support, unit testing, espresso idling resources, and compatibility with RxJava and LiveData. Ideal for state management and side-effect handling.
Orbit is a simple, type-safe MVI framework for Kotlin Multiplatform, enabling shared business logic across Android, iOS and desktop. With a Redux/MVI-inspired unidirectional data flow, it streamlines state management within MVVM—think of it as MVVM+.
Key features:
// Core of Orbit, providing state management and unidirectional data flow (multiplatform)
implementation("org.orbit-mvi:orbit-core:<latest-version>")
// Integrates Orbit with Android and Common ViewModel for lifecycle-aware state handling (Android, iOS, desktop)
implementation("org.orbit-mvi:orbit-viewmodel:<latest-version>")
// Enables Orbit support for Jetpack Compose and Compose Multiplatform (Android, iOS, desktop)
implementation("org.orbit-mvi:orbit-compose:<latest-version>")
// Simplifies testing with utilities for verifying state and event flows (multiplatform)
testImplementation("org.orbit-mvi:orbit-test:<latest-version>")data class CalculatorState(
val total: Int = 0
)
sealed class CalculatorSideEffect {
data class Toast(val text: String) : CalculatorSideEffect()
}container field and use the ViewModel.container factory
function to build an Orbit
Container
in your
ContainerHost
class CalculatorViewModel: ContainerHost<CalculatorState, CalculatorSideEffect>, ViewModel() {
// Include `orbit-viewmodel` for the factory function
override val container = container<CalculatorState, CalculatorSideEffect>(CalculatorState())
fun add(number: Int) = intent {
postSideEffect(CalculatorSideEffect.Toast("Adding $number to ${state.total}!"))
reduce {
state.copy(total = state.total + number)
}
}
}We have used an Android ViewModel as the most common example, but there is no
requirement to do so.
class CalculatorActivity: AppCompatActivity() {
// Example of injection using koin, your DI system might differ
private val viewModel by viewModel<CalculatorViewModel>()
override fun onCreate(savedState: Bundle?) {
...
addButton.setOnClickListener { viewModel.add(1234) }
viewModel.observe(state = ::render, sideEffect = ::handleSideEffect)
}
private fun render(state: CalculatorState) {
...
}
private fun handleSideEffect(sideEffect: CalculatorSideEffect) {
when (sideEffect) {
is CalculatorSideEffect.Toast -> toast(sideEffect.text)
}
}
}With Jetpack Compose wire up the ViewModel as follows:
@Composable
fun CalculatorScreen(viewModel: CalculatorViewModel) {
val state = viewModel.collectAsState().value
viewModel.collectSideEffect { handleSideEffect(it) }
// render UI using data from 'state'
...
}
private fun handleSideEffect(sideEffect: CalculatorSideEffect) {
when (sideEffect) {
is CalculatorSideEffect.Toast -> toast(sideEffect.text)
}
}Please read contributing for details on our code of conduct, and the process for submitting pull requests to us.
We use SemVer for versioning. For the versions available, see the tags on this repository.
This project is licensed under the Apache License, Version 2.0 - see the license file for details
Orbit is a simple, type-safe MVI framework for Kotlin Multiplatform, enabling shared business logic across Android, iOS and desktop. With a Redux/MVI-inspired unidirectional data flow, it streamlines state management within MVVM—think of it as MVVM+.
Key features:
// Core of Orbit, providing state management and unidirectional data flow (multiplatform)
implementation("org.orbit-mvi:orbit-core:<latest-version>")
// Integrates Orbit with Android and Common ViewModel for lifecycle-aware state handling (Android, iOS, desktop)
implementation("org.orbit-mvi:orbit-viewmodel:<latest-version>")
// Enables Orbit support for Jetpack Compose and Compose Multiplatform (Android, iOS, desktop)
implementation("org.orbit-mvi:orbit-compose:<latest-version>")
// Simplifies testing with utilities for verifying state and event flows (multiplatform)
testImplementation("org.orbit-mvi:orbit-test:<latest-version>")data class CalculatorState(
val total: Int = 0
)
sealed class CalculatorSideEffect {
data class Toast(val text: String) : CalculatorSideEffect()
}container field and use the ViewModel.container factory
function to build an Orbit
Container
in your
ContainerHost
class CalculatorViewModel: ContainerHost<CalculatorState, CalculatorSideEffect>, ViewModel() {
// Include `orbit-viewmodel` for the factory function
override val container = container<CalculatorState, CalculatorSideEffect>(CalculatorState())
fun add(number: Int) = intent {
postSideEffect(CalculatorSideEffect.Toast("Adding $number to ${state.total}!"))
reduce {
state.copy(total = state.total + number)
}
}
}We have used an Android ViewModel as the most common example, but there is no
requirement to do so.
class CalculatorActivity: AppCompatActivity() {
// Example of injection using koin, your DI system might differ
private val viewModel by viewModel<CalculatorViewModel>()
override fun onCreate(savedState: Bundle?) {
...
addButton.setOnClickListener { viewModel.add(1234) }
viewModel.observe(state = ::render, sideEffect = ::handleSideEffect)
}
private fun render(state: CalculatorState) {
...
}
private fun handleSideEffect(sideEffect: CalculatorSideEffect) {
when (sideEffect) {
is CalculatorSideEffect.Toast -> toast(sideEffect.text)
}
}
}With Jetpack Compose wire up the ViewModel as follows:
@Composable
fun CalculatorScreen(viewModel: CalculatorViewModel) {
val state = viewModel.collectAsState().value
viewModel.collectSideEffect { handleSideEffect(it) }
// render UI using data from 'state'
...
}
private fun handleSideEffect(sideEffect: CalculatorSideEffect) {
when (sideEffect) {
is CalculatorSideEffect.Toast -> toast(sideEffect.text)
}
}Please read contributing for details on our code of conduct, and the process for submitting pull requests to us.
We use SemVer for versioning. For the versions available, see the tags on this repository.
This project is licensed under the Apache License, Version 2.0 - see the license file for details