{"id":13536633,"url":"https://github.com/respawn-app/FlowMVI","last_synced_at":"2025-04-02T03:30:57.041Z","repository":{"id":38298564,"uuid":"477143989","full_name":"respawn-app/FlowMVI","owner":"respawn-app","description":"Architecture Framework for Kotlin. Reuse every line of code. Handle all errors automatically. No boilerplate. Build features in minutes. Analytics, metrics, debugging in 3 lines of code. Make all code thread-safe. 50+ features. ","archived":false,"fork":false,"pushed_at":"2025-03-13T13:33:47.000Z","size":5649,"stargazers_count":511,"open_issues_count":21,"forks_count":18,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-13T14:27:46.066Z","etag":null,"topics":["android","android-architecture","android-library","architecture","compose","compose-multiplatform","coroutines","ios","jvm","kmp","kotlin","kotlin-coroutines","kotlin-multiplatform","multiplatform","mvi","mvvm","mvvm-architecture","udf","wasm"],"latest_commit_sha":null,"homepage":"https://opensource.respawn.pro/FlowMVI/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/respawn-app.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-02T18:50:56.000Z","updated_at":"2025-03-13T05:51:19.000Z","dependencies_parsed_at":"2024-01-03T12:24:22.325Z","dependency_job_id":"9ae24a0d-b928-4d66-8089-6546bbbc8794","html_url":"https://github.com/respawn-app/FlowMVI","commit_stats":null,"previous_names":[],"tags_count":64,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/respawn-app%2FFlowMVI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/respawn-app%2FFlowMVI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/respawn-app%2FFlowMVI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/respawn-app%2FFlowMVI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/respawn-app","download_url":"https://codeload.github.com/respawn-app/FlowMVI/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246750944,"owners_count":20827806,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-architecture","android-library","architecture","compose","compose-multiplatform","coroutines","ios","jvm","kmp","kotlin","kotlin-coroutines","kotlin-multiplatform","multiplatform","mvi","mvvm","mvvm-architecture","udf","wasm"],"created_at":"2024-08-01T09:00:45.880Z","updated_at":"2025-04-02T03:30:57.026Z","avatar_url":"https://github.com/respawn-app.png","language":"Kotlin","funding_links":[],"categories":["Libraries","By Language"],"sub_categories":["Architecture","🏗 Architecture","Kotlin"],"readme":"![FlowMVI Framework Banner](https://opensource.respawn.pro/FlowMVI/banner.webp)\n\n[![CI](https://github.com/respawn-app/FlowMVI/actions/workflows/ci.yml/badge.svg)](https://github.com/respawn-app/FlowMVI/actions/workflows/ci.yml)\n![License](https://img.shields.io/github/license/respawn-app/flowMVI)\n![GitHub last commit](https://img.shields.io/github/last-commit/respawn-app/FlowMVI)\n![Issues](https://img.shields.io/github/issues/respawn-app/FlowMVI)\n![GitHub top language](https://img.shields.io/github/languages/top/respawn-app/flowMVI)\n[![AndroidWeekly #563](https://androidweekly.net/issues/issue-563/badge)](https://androidweekly.net/issues/issue-563/)\n[![Slack channel](https://img.shields.io/badge/Chat-Slack-orange.svg?style=flat\u0026logo=slack)](https://kotlinlang.slack.com/messages/flowmvi/)\n\n![badge][badge-android] ![badge][badge-jvm] ![badge][badge-js] ![badge][badge-nodejs] ![badge][badge-linux] ![badge][badge-windows] ![badge][badge-ios] ![badge][badge-mac] ![badge][badge-watchos] ![badge][badge-tvos] ![badge][badge-wasm]\n\nFlowMVI is a Kotlin Multiplatform architectural framework based on coroutines.\nIt enables you to extend your business logic with reusable plugins, handle errors,\nachieve thread-safety, and more. It takes about 10 minutes to get started.\n\n## ⚡️ Quickstart:\n\n* Latest version:\n  [![Maven Central](https://img.shields.io/maven-central/v/pro.respawn.flowmvi/core?label=Maven%20Central)](https://central.sonatype.com/namespace/pro.respawn.flowmvi)\n* Documentation:\n  [![Docs](https://img.shields.io/website?down_color=red\u0026down_message=Offline\u0026label=Docs\u0026up_color=green\u0026up_message=Online\u0026url=https%3A%2F%2Fopensource.respawn.pro%2FFlowMVI%2F%23%2F)](https://opensource.respawn.pro/FlowMVI/quickstart)\n* KDoc:\n  [![Javadoc](https://javadoc.io/badge2/pro.respawn.flowmvi/core/javadoc.svg)](https://opensource.respawn.pro/FlowMVI/javadocs/index.html)\n* Sample App (See features) ![badge-wasm]:\n  [![Static Badge](https://img.shields.io/badge/Click_Me-Click_Me?style=flat\u0026color=00b147)](https://opensource.respawn.pro/FlowMVI/sample/)\n* Ask questions on\n  [![Slack](https://img.shields.io/badge/Chat-Slack-orange.svg?style=flat\u0026logo=slack)](https://kotlinlang.slack.com/messages/flowmvi/)\n\n\u003cdetails\u003e\n\u003csummary\u003eVersion catalogs\u003c/summary\u003e\n\n```toml\n[versions]\nflowmvi = \"\u003c Badge above 👆🏻 \u003e\"\n\n[dependencies]\n# Core KMP module\nflowmvi-core = { module = \"pro.respawn.flowmvi:core\", version.ref = \"flowmvi\" }\n# Test DSL\nflowmvi-test = { module = \"pro.respawn.flowmvi:test\", version.ref = \"flowmvi\" }\n# Compose multiplatform\nflowmvi-compose = { module = \"pro.respawn.flowmvi:compose\", version.ref = \"flowmvi\" }\n# Android (common + view-based)\nflowmvi-android = { module = \"pro.respawn.flowmvi:android\", version.ref = \"flowmvi\" }\n# Multiplatform state preservation\nflowmvi-savedstate = { module = \"pro.respawn.flowmvi:savedstate\", version.ref = \"flowmvi\" }\n# Remote debugging client\nflowmvi-debugger-client = { module = \"pro.respawn.flowmvi:debugger-plugin\", version.ref = \"flowmvi\" }\n# Essenty (Decompose) integration\nflowmvi-essenty = { module = \"pro.respawn.flowmvi:essenty\", version.ref = \"flowmvi\" }\nflowmvi-essenty-compose = { module = \"pro.respawn.flowmvi:essenty-compose\", version.ref = \"flowmvi\" }\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eGradle DSL\u003c/summary\u003e\n\n```kotlin\ndependencies {\n    val flowmvi = \"\u003c Badge above 👆🏻 \u003e\"\n    // Core KMP module\n    commonMainImplementation(\"pro.respawn.flowmvi:core:$flowmvi\")\n    // compose multiplatform\n    commonMainImplementation(\"pro.respawn.flowmvi:compose:$flowmvi\")\n    // saving and restoring state\n    commonMainImplementation(\"pro.respawn.flowmvi:savedstate:$flowmvi\")\n    // essenty integration\n    commonMainImplementation(\"pro.respawn.flowmvi:essenty:$flowmvi\")\n    commonMainImplementation(\"pro.respawn.flowmvi:essenty-compose:$flowmvi\")\n    // testing DSL\n    commonTestImplementation(\"pro.respawn.flowmvi:test:$flowmvi\")\n    // android integration\n    androidMainImplementation(\"pro.respawn.flowmvi:android:$flowmvi\")\n    // remote debugging client\n    androidDebugImplementation(\"pro.respawn.flowmvi:debugger-plugin:$flowmvi\")\n}\n```\n\n\u003c/details\u003e\n\n## 🚀 Why FlowMVI?\n\nUsually architecture frameworks mean boilerplate and support difficulty for marginal benefits of \"clean code\".\nFlowMVI does not dictate what your code should do or look like.\nInstead, this library focuses on building a supporting infrastructure to enable new possibilities for your app.\n\nHere's what you get:\n\n* Powerful Plug-In system to automate processes and **reuse any business logic** you desire\n    * Create automatic analytics handlers, websocket connections, error handling mechanisms, or anything else **once**\n      and reuse them throughout your whole project automatically\n* Automatically **recover from any errors** and report them to analytics.\n* Build fully **async, reactive and parallel apps** - with no manual thread synchronization required!\n* Create **multiplatform business logic** components with pluggable UI\n* Automatic multiplatform system **lifecycle handling**\n* Out of the box **debugging, logging, caching and long-running tasks** support\n* Debounce, retry, batch, throttle, conflate, monitor, **modify any operations** automatically\n* **Compress, persist, and restore state** automatically on any platform\n* **No base classes, complicated interfaces**, or factories of factories - logic is declarative and built with a DSL\n* Build **Restartable, reusable business logic components** with no external dependencies or dedicated lifecycles\n* Create **compile-time safe state machines** with a readable DSL. Forget about casts, inconsistent states, and `null`s\n* First class **Compose Multiplatform support** optimized for performance and ease of use\n* Use both **MVVM+** (functional) or **MVI** (model-driven) style of programming\n* Share, distribute, disable, **manage side-effects** based on your team's needs\n* Dedicated **IDE Plugin for debugging and codegen** and app for Windows, Linux, MacOS\n* **Integration with popular libraries**, such as [Decompose (Essenty)](https://github.com/arkivanov/Decompose)\n* The core **library has no dependencies** except kotlin coroutines.\n* Core library is fully covered by **hundreds of tests**\n* **Minimal performance overhead**, equal to using a simple Channel, with regular benchmarking\n* Collect, monitor and report **performance metrics** automatically (upcoming).\n* **Test any business logic** using clean, declarative DSL.\n* Learn more by exploring the [sample app](https://opensource.respawn.pro/FlowMVI/sample/) in your browser\n\n## 👀 How to get started?\n\nAll you have to do is:\n\n### 1. Define a Contract:\n\n```kotlin\nsealed interface State : MVIState {\n\n    data object Loading : State\n    data class Error(val e: Exception) : State\n    data class Content(val counter: Int = 0) : State\n}\n\nsealed interface Intent : MVIIntent {\n    data object ClickedCounter : Intent\n}\n\nsealed interface Action : MVIAction {\n    data class ShowMessage(val message: String) : Action\n}\n```\n\n### 2. Declare your business logic:\n\n```kotlin\nval counterStore = store(initial = State.Loading, scope = coroutineScope) {\n\n    install(analyticsPlugin) // install plugins you need\n\n    recover { e: Exception -\u003e // recover from errors\n        updateState { State.Error(e) }\n        null\n    }\n    init { // load data\n        updateState {\n            State.Content(counter = repository.loadCounter())\n        }\n    }\n    reduce { intent: Intent -\u003e // respond to events\n        when (intent) {\n            is ClickedCounter -\u003e updateState\u003cState.Content, _\u003e {\n                action(ShowMessage(\"Incremented!\"))\n\n                copy(counter = counter + 1)\n            }\n        }\n    }\n}\n\nstore.intent(ClickedCounter)\n```\n\n### 3. Scale your app\n\nWith FlowMVI, complexity **does not grow** no matter how many features you add.\nAdding a new feature is as simple as calling a function.\n\n\u003cdetails\u003e\n\u003csummary\u003eAdvanced configuration example\u003c/summary\u003e\n\n```kotlin\nclass CounterContainer(\n    private val repo: CounterRepository, // inject dependencies\n) {\n    val store = store\u003cCounterState, CounterIntent, CounterAction\u003e(initial = Loading) {\n\n        configure {\n            // use various side-effect strategies\n            actionShareBehavior = Distribute()\n\n            // checks and verifies your business logic for you\n            debuggable = true\n\n            // make the store fully async, parallel and thread-safe\n            parallelIntents = true\n            coroutineContext = Dispatchers.Default\n            stateStrategy = Atomic()\n        }\n\n        // out of the box logging\n        enableLogging()\n\n        // debug using the IDE plugin\n        enableRemoteDebugging()\n\n        // undo / redo any operation\n        val undoRedo = undoRedo()\n\n        // manage long-running jobs\n        val jobManager = manageJobs\u003cCounterJob\u003e()\n\n        // save and restore the state automatically\n        serializeState(\n            path = repo.cacheFile(\"counter\"),\n            serializer = DisplayingCounter.serializer(),\n        )\n\n        // perform long-running tasks on startup\n        init {\n            repo.startTimer()\n        }\n\n        // save resources when there are no subscribers\n        whileSubscribed {\n            repo.timer.collect {\n                updateState\u003cDisplayingCounter, _\u003e {\n                    copy(timer = timer)\n                }\n            }\n        }\n\n        // lazily evaluate and cache values, even when the method is suspending.\n        val pagingData by cache {\n            repo.getPagedDataSuspending()\n        }\n\n        // testable reducer as a function\n        reduce { intent: CounterIntent -\u003e\n            when (intent) {\n                // typed state update prevents races and allows using sealed class hierarchies for LCE\n                is ClickedCounter -\u003e updateState\u003cDisplayingCounter, _\u003e {\n                    copy(counter = counter + 1)\n                }\n            }\n        }\n\n        // cleanup resources\n        deinit {\n            repo.stopTimer()\n        }\n\n        // and 30+ more options to choose from...\n    }\n}\n```\n\n\u003c/details\u003e\n\n## Extend your logic with Plugins\n\nPowerful DSL allows you to hook into various events and amend any part of your logic:\n\n```kotlin\nfun analyticsPlugin(analytics: Analytics) = plugin\u003cMVIState, MVIIntent, MVIAction\u003e {\n    onStart {\n        analytics.logScreenView(config.name) // name of the screen\n    }\n    onIntent { intent -\u003e\n        analytics.logUserAction(intent.name)\n    }\n    onException { e -\u003e\n        analytics.logError(e)\n    }\n    onSubscribe {\n        analytics.logEngagementStart()\n    }\n    onUnsubscribe {\n        analytics.logEngagementEnd()\n    }\n    onStop {\n        analytics.logScreenLeave()\n    }\n}\n```\n\nNever write analytics, debugging, or state persistence code again.\n\n## Compose Multiplatform Support\n\n![badge][badge-android] ![badge][badge-ios] ![badge][badge-mac] ![badge][badge-jvm] ![badge][badge-wasm] ![badge][badge-js]\n\nUsing FlowMVI with Compose is a matter of one line of code:\n\n```kotlin\n@Composable\nfun CounterScreen() {\n    val store = counterStore\n\n    // subscribe to store based on system lifecycle - on any platform\n    val state by store.subscribe { action -\u003e\n        when (action) {\n            is ShowMessage -\u003e /* ... */\n        }\n    }\n\n    when (state) {\n        is DisplayingCounter -\u003e {\n            Button(onClick = { store.intent(ClickedCounter) }) {\n                Text(\"Counter: ${state.counter}\")\n            }\n        }\n    }\n}\n```\n\nEnjoy testable UI and free `@Preview`s.\n\n### Android Support\n\nNo more subclassing `ViewModel`. Use `StoreViewModel` instead and make your business logic multiplatform.\n\n```kotlin\nval module = module { // Koin example\n    factoryOf(::CounterContainer)\n    viewModel(qualifier\u003cCounterContainer\u003e()) { StoreViewModel(get\u003cCounterContainer\u003e()) }\n}\n\nclass ScreenFragment : Fragment() {\n\n    private val vm by viewModel(qualifier\u003cCounterContainer\u003e())\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        subscribe(vm, ::consume, ::render)\n    }\n\n    private fun render(state: CounterState) {\n        // update your views\n    }\n\n    private fun consume(action: CounterAction) {\n        // handle actions\n    }\n}\n```\n\n## Testing DSL\n\nFinally stop writing UI tests and replace them with unit tests:\n\n### Test Stores\n\n```kotlin\nstore.subscribeAndTest {\n    // turbine + kotest example\n    ClickedCounter resultsIn {\n        states.test {\n            awaitItem() shouldBe State(counter = 1)\n        }\n        actions.test {\n            awaitItem() shouldBe ShowMessage\n        }\n    }\n}\n```\n\n### Test plugins\n\n```kotlin\nval timer = Timer()\ntimerPlugin(timer).test(Loading) {\n\n    onStart()\n\n    // time travel keeps track of all plugin operations for you\n    assert(timeTravel.starts == 1)\n    assert(state is DisplayingCounter)\n    assert(timer.isStarted)\n\n    onStop(null)\n\n    assert(!timer.isStarted)\n}\n```\n\n## Debugger IDE Plugin + App\n\nIDE plugin generates code and lets you debug and control your app remotely:\n[![Plugin](https://img.shields.io/jetbrains/plugin/v/25766?style=flat)](https://plugins.jetbrains.com/plugin/25766-flowmvi)\n\n\u003cvideo\n  src='https://github.com/user-attachments/assets/05f8efdb-d125-4c4a-9bda-79875f22578f'\n  controls\n  width=\"100%\"\n  alt=\"FlowMVI IDE Plugin Demo\"\u003e\n  Your browser does not support the video element. You can view the demo at our website.\n\u003c/video\u003e\n\n## People love the library:\n\n\u003ca href=\"https://star-history.com/#respawn-app/flowmvi\u0026Date\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=respawn-app/flowmvi\u0026type=Date\u0026theme=dark\" /\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=respawn-app/flowmvi\u0026type=Date\" /\u003e\n    \u003cimg alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=respawn-app/flowmvi\u0026type=Date\" /\u003e\n  \u003c/picture\u003e\n\u003c/a\u003e\n\n## Ready to try?\n\nBegin by reading the [Quickstart Guide](https://opensource.respawn.pro/FlowMVI/quickstart).\n\n----\n\n## License\n\n```\n   Copyright 2022-2025 Respawn Team and contributors\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       https://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n```\n\n[badge-android]: https://img.shields.io/badge/-android-6EDB8D.svg?style=flat\n\n[badge-android-native]: https://img.shields.io/badge/support-[AndroidNative]-6EDB8D.svg?style=flat\n\n[badge-jvm]: https://img.shields.io/badge/-jvm-DB413D.svg?style=flat\n\n[badge-js]: https://img.shields.io/badge/-js-F8DB5D.svg?style=flat\n\n[badge-js-ir]: https://img.shields.io/badge/support-[IR]-AAC4E0.svg?style=flat\n\n[badge-nodejs]: https://img.shields.io/badge/-nodejs-68a063.svg?style=flat\n\n[badge-linux]: https://img.shields.io/badge/-linux-2D3F6C.svg?style=flat\n\n[badge-windows]: https://img.shields.io/badge/-windows-4D76CD.svg?style=flat\n\n[badge-wasm]: https://img.shields.io/badge/-wasm-624FE8.svg?style=flat\n\n[badge-apple-silicon]: https://img.shields.io/badge/support-[AppleSilicon]-43BBFF.svg?style=flat\n\n[badge-ios]: https://img.shields.io/badge/-ios-CDCDCD.svg?style=flat\n\n[badge-mac]: https://img.shields.io/badge/-macos-111111.svg?style=flat\n\n[badge-watchos]: https://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat\n\n[badge-tvos]: https://img.shields.io/badge/-tvos-808080.svg?style=flat\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frespawn-app%2FFlowMVI","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frespawn-app%2FFlowMVI","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frespawn-app%2FFlowMVI/lists"}