
Enables efficient state management with an MVI framework, featuring state rendering, action dispatching, event handling, and middleware integration for customizable application architecture, including support for Compose.
Tart is Kotlin Multiplatform MVI Framework.
implementation("io.github.hkusu.muscat:muscat-core:<latest-version>")
// or, in libs.versions.toml
tart-core = { module = "io.github.hkusu.muscat:muscat-core", version = "<latest-version>" }
TODO
You can use .state, .event, .dispatch() provided by Store, but we have a class for Compose.
implementation("io.github.hkusu.muscat:muscat-compose:<latest-version>")
// or, in libs.versions.toml
tart-compose = { module = "io.github.hkusu.muscat:muscat-compose", version = "<latest-version>" }
Create an instance of the ComposeStore from a regular Store instance using the ComposeStore#create() function.
For example, if you have a Store in your ViewModel, it would look like this:
@AndroidEntryPoint
class YourActivity : ComponentActivity() {
private val yourViewModel: YourViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// create ComposeStore instance
val store = ComposeStore.create(YourViewModel.store)
MyApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
) {
// pass as an argument to child Composable component
YourChildComposableComponent(
store = store,
)
// ...
Use ComposeStore.render() method.
Specify the target State using generics.
store.render<YourState.Stable> {
YourComposableComponent()
}
If it does not match the current State, the { } block will not be executed.
Therefore, you can define views for each State side by side.
store.render<YourState.Loading> {
YourComposableComponent_A()
}
store.render<YourState.Stable> {
YourComposableComponent_B()
}
State properties can be accessed with this scope.
store.render<YourState.Stable> {
YourComposableComponent(url = this.url) // this. can be omitted
}
Use ComposeStore.dispatch() method.
Button(
onClick = { store.dispatch(YourAction.ClickButton) },
) {
// ...
Use ComposeStore.handle() method.
Specify the target State using generics.
store.handle<YourEvent.ShowToast> { event ->
// do something..
}
Event properties can be accessed with this scope.
You can also subscribe to parent types.
store.handle<YourEvent> { event ->
when (event) {
is YourEvent.ShowToast -> {
// do something..
}
is YourEvent.GoBack -> {
// do something..
}
// ...
}
}
Use ComposeStore#mock() function with target State.
@Preview
@Composable
fun SomePreview() {
MyApplicationTheme {
YourChildComposableComponent(
store = ComposeStore.mock(
state = YourState.Stable,
),
)
}
}
You can create functions linked to State etc. on the Store.
To do this, create a class with the Middleware interface and override the necessary methods.
class YourMiddleware<S : State, A : Action, E : Event> : Middleware<S, A, E> {
override suspend fun runAfterStateChange(state: S, prevState: S) {
// do something..
}
}
The :tart-logging module in this repository is an example of Middleware that does simple logging.
TODO
Tart is Kotlin Multiplatform MVI Framework.
implementation("io.github.hkusu.muscat:muscat-core:<latest-version>")
// or, in libs.versions.toml
tart-core = { module = "io.github.hkusu.muscat:muscat-core", version = "<latest-version>" }
TODO
You can use .state, .event, .dispatch() provided by Store, but we have a class for Compose.
implementation("io.github.hkusu.muscat:muscat-compose:<latest-version>")
// or, in libs.versions.toml
tart-compose = { module = "io.github.hkusu.muscat:muscat-compose", version = "<latest-version>" }
Create an instance of the ComposeStore from a regular Store instance using the ComposeStore#create() function.
For example, if you have a Store in your ViewModel, it would look like this:
@AndroidEntryPoint
class YourActivity : ComponentActivity() {
private val yourViewModel: YourViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// create ComposeStore instance
val store = ComposeStore.create(YourViewModel.store)
MyApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
) {
// pass as an argument to child Composable component
YourChildComposableComponent(
store = store,
)
// ...
Use ComposeStore.render() method.
Specify the target State using generics.
store.render<YourState.Stable> {
YourComposableComponent()
}
If it does not match the current State, the { } block will not be executed.
Therefore, you can define views for each State side by side.
store.render<YourState.Loading> {
YourComposableComponent_A()
}
store.render<YourState.Stable> {
YourComposableComponent_B()
}
State properties can be accessed with this scope.
store.render<YourState.Stable> {
YourComposableComponent(url = this.url) // this. can be omitted
}
Use ComposeStore.dispatch() method.
Button(
onClick = { store.dispatch(YourAction.ClickButton) },
) {
// ...
Use ComposeStore.handle() method.
Specify the target State using generics.
store.handle<YourEvent.ShowToast> { event ->
// do something..
}
Event properties can be accessed with this scope.
You can also subscribe to parent types.
store.handle<YourEvent> { event ->
when (event) {
is YourEvent.ShowToast -> {
// do something..
}
is YourEvent.GoBack -> {
// do something..
}
// ...
}
}
Use ComposeStore#mock() function with target State.
@Preview
@Composable
fun SomePreview() {
MyApplicationTheme {
YourChildComposableComponent(
store = ComposeStore.mock(
state = YourState.Stable,
),
)
}
}
You can create functions linked to State etc. on the Store.
To do this, create a class with the Middleware interface and override the necessary methods.
class YourMiddleware<S : State, A : Action, E : Event> : Middleware<S, A, E> {
override suspend fun runAfterStateChange(state: S, prevState: S) {
// do something..
}
}
The :tart-logging module in this repository is an example of Middleware that does simple logging.
TODO