Skip to content

A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

License

Notifications You must be signed in to change notification settings

Mimoidu/FlowMVI

 
 

Repository files navigation

Flow MVI

CI Docs Javadoc License GitHub last commit Issues GitHub top language CodeFactor AndroidWeekly #556

FlowMVI is a Kotlin Multiplatform MVI implementation based on coroutines with a few main goals:

  1. Being simple to understand, implement and use
  2. Following the Principle of Least Responsibility - all communication happens through strictly defined contract
  3. Featuring a clean and readable DSL
  4. Being thread-safe but asynchronous

Let's get started:

Maven Central

[versions]
flowmvi = "< Badge above 👆🏻 >"

[dependencies]
flowmvi-core = { module = "pro.respawn.flowmvi:core", version.ref = "flowmvi" } # multiplatform
flowmvi-android = { module = "pro.respawn.flowmvi:android", version.ref = "flowmvi" } # common android
flowmvi-view = { module = "pro.respawn.flowmvi:android-view", version.ref = "flowmvi" } # view-based android
flowmvi-compose = { module = "pro.respawn.flowmvi:android-compose", version.ref = "flowmvi" }  # compose

Core:

sealed interface ScreenState : MVIState {
    data object Loading : ScreenState
    data class Error(e: Exception) : ScreenState
    data class DisplayingCounter(val counter: Int) : ScreenState
}

sealed interface ScreenIntent : MVIIntent {
    data object ClickedCounter : ScreenIntent
}

sealed interface ScreenAction : MVIAction {
    data class ShowMessage(val message: String) : ScreenAction
}


val store by launchedStore<ScreenState, ScreenIntent, ScreenAction>(
    scope = eventProcessingCoroutineScope,
    initial = Loading,
    reduce = { intent -> /*...*/ },
)

// somewhere in the ui layer...
store.subscribe(
    consumerCoroutineScope,
    consume = { action -> /* ... */ },
    render = { state -> /* ... */ },
)

Android (Compose):

class ScreenViewModel : MVIViewModel<ScreenState, ScreenIntent, ScreenAction>(initialState = Loading) {

    override suspend fun reduce(intent: ScreenIntent): Unit = when (intent) {
        is ClickedCounter -> updateState<DisplayingCounter> { //this -> DisplayingCounter

            ShowMessage("Incremented counter").send()

            copy(counter = counter + 1)
        }
        /* ... */
    }
}

@Composable
fun ComposeScreen() = MVIComposable(
    provider = getViewModel<ScreenViewModel>(),
) { state ->

    consume { action ->
        when (action) {
            is ShowMessage -> {
                /* ... */
            }
        }
    }

    when (state) {
        is DisplayingCounter -> {
            Button(onClick = { ClickedCounter.send() }) {
                Text("Counter: ${state.counter}") // render state,
            }
        }
    }
}

Android (View):

// ViewModel and Model classes have not changed

class ScreenFragment: Fragment(), MVIView<ScreenState, ScreenIntent, ScreenAction> {

    override val provider by viewModel<ScreenViewModel>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        subscribe() // One-liner for store subscription. Lifecycle-aware and efficient.
    }

    override fun render(state: ScreenState) {
        // update your views
    }

    override fun consume(action: ScreenAction) {
        // handle actions
    }
}

And that's it!
For more information and sample code, see the Documentation.

License

   Copyright 2022 Respawn Team and contributors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

About

A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Kotlin 100.0%