Releases: respawn-app/FlowMVI
3.3.0-alpha03
This is a small release that downgrades kotlin to 2.2.21 until https://youtrack.jetbrains.com/issue/KT-83395 is resolved.
If you target 2.3.0, keep using alpha02. If you face KT-83395 or any other issues, consider alpha03.
3.3.0-alpha02
📊 Performance Metrics
New artifact: :metrics is here!
With FlowMVI in just a 5 lines of code you can now set up metrics collection to understand the performance and usage patterns of your app!
FlowMVI metrics collect over 66 different stats including speed of intent handling, state updates, error frequencies, data loading delays, bootstrapping time and lifecycle events, and more, then lets you upload to any popular metrics ingestion service such as OpenTelemetry, Prometheus, OpenMetrics etc, and visualize them in dashboards:
val metrics = collectMetrics(
reportingScope = applicationScope
)
reportMetrics(
metrics = metrics,
sink = OtlpJsonSink(BackendSink()),
)The debugger IDE plugin and desktop apps have been upgraded to support the ingestion of metrics and also visualize them in a similar way out of the box, with minimal setup required to send metrics to the debugger:
Screen.Recording.2025-12-14.at.11.06.00.AM.mov
Metrics are being benchmarked and are optimized to minimally influence the performance of your business logic, so that you can just plug them in your store configuration once and enjoy the benefits everywhere.
Get started by reading the documentation
🚀 New Hooks:
onActionDispatchhook will trigger when side-effects are dequeued, as opposed toonActionwhich triggers when enqueuedonIntentEnqueuehook will trigger when intents are enqueued, as opposed toonIntentwhich triggers when dequeued
ShutdownContext
ShutdownContext now implements StoreLifecycle which holds a reference to the last store lifetime. Previously, any lifecycle was
grabbed from the Store itself which may not be the one that onStop was invoked with. This should improve reliability.
This may be a behavioral breaking change if you relied on StoreLifecycle inside of onStop. Worth double-checking your onStop hooks to be safe.
🚢 Other New Stuff
Note: Debugger of version 3.3.0 may be incompatible with FlowMVI 3.2x and older, downgrade or upgrade one of these to fix.
- Debugger will now merge events from stores if they have the same name
- Add
debounceIntentsdecorator - Migrate
PluginTestScopeto be an interface instead of class. - Add validation to BatchIntentsDecorator modes. Worth re-checking that you don't pass invalid values there before shipping.
- Each
StoreConfigurationnow hasstoreIda unique UUID of the store instance. Using static unique names is now even more important with metrics to aggregate events per-screen reliably.
🐞 Bug Fixes
Important fix: onUndeliveredAction callback wasn't properly wired and was never invoked since 3.1.0, fixed with this release + tests added that should prevent such issues in the future.
- add failsafe to debug client store when events can't be deserialized
- add failsafe to debug server when events can't be deserialized
- let StoreConfiguration's state be covariant
Deps
- Kotlin 2.3.0-RC3
- Compose 1.10-rc1
- Ktor 3.3.3
Safe upgrade checklist
- Check
onStopcallbacks that useStoreLifecyclemethodsawaitUntilClosedetc for correctness - Upgrade debugger IDE plugin
- Check
onUndeliveredActionusages which should now work - Check
batchIntentsdecorator configuration for validity of passed values - Wire/migrate to
onActionDispatchoronIntentEnqueuebased on your needs (analytics plugins etc)
Rest of the changelog
- add metrics decorator
- feat!: let ShutdownContext implement StoreLifecycle and pass one-time shutdown context there
- refactor metrics collector for safe event handling across restarts
- implement Metrics api for decoupling metric collection and flushing
- implement default metrics flusher plugin
- implement OpenMetricsSink
- implement otel metrics
- add metrics schema versioning surface
- add metrics showcase to the sample app
- implement benchmarks evaluating metrics collection overhead
- add debounce intents decorator
- feat!: let ShutdownContext implement StoreLifecycle and pass one-time shutdown context there
- add debounce intents decorator
- feat!: add validation to BatchIntentsDecorator modes
- add wasm-wasi target to metrics and common debugger code
- introduce stable store IDs for debugging and metrics
- enable wasmJs and js for debugger plugin module
- implement DebuggerSink for metrics, wire metrics to sample app
- add
storeNameparameter to client events - accumulate metrics in debug server
- implement metrics visualization in the debugger
- add documentation for metrics visualization in debugger
- update deps
- downgrade kotest due to kotest/kotest#5203
- apply hack for JetBrains/intellij-platform-gradle-plugin#2062
- downgrade intellij plugin due to JetBrains/intellij-platform-gradle-plugin#2062
- migrate compose deps to version catalog
- replace material debugger icons with custom iconpack
- migrate sample app from material icons extended dependency
- temporarily set logging level to quiet to silence fp warning
- cover enqueue dispatch hooks with tests
- update agents and SECURITY.md
- provide fixed timestamp for tests to otel metrics mapper
- remove fluxo benchmarks
- add tests for sinks and basic apis
- more tests for edge cases of sinks
- add tests for quantiles/ema/perfmetrics
- cleanup to improve testability of new metrics internals
- add tests for debounce decorator
- add test utils for metrics
- enhance and simplify test dsl for new tests
- add utils to test metrics with real store
- add full test suite for the MetricsCollector and plugin composition
- align collector tests
- update readme with metrics and more benefits, reduce size
- temp remove claude workflow
- update agents and SECURITY.md
- added testing guidance for agents
- create a documentation page on Metrics
Full Changelog: 3.2.1...3.3.0-alpha02
3.2.1
Breaking changes
- Android minimum API requirement is now 23, because Google forced us by upgrading it in activity-ktx.
🚀 New Features
Enforce must-use return value (#152).
If you enable compiler flag -Xreturn-value-checker=check, the IDE will warn you when you create a store/subscription/plugin and not use it.
- add lambda intent emit { } lambda overload
- improve return type in store.test { } with
SubscriptionAwareto test subscriptions. You can now accesssubscriberCountin tests. - Batch intent decorator now exposes
onUnhandledIntent. This is used when the intent is batched, but then was unhandled. This is likely a temporary solution to "catch" those intents, as due to intents being deferred, we can' use our regularonUndeliveredIntentfunctions. - implement new sample showcasing serialized state transactions
🐞 Bug Fixes
This update includes many important fixes for decorators and child stores.
- #173 ensure delegate unsubscribes child stores
- fix off-by-one in batch intents decorator
- harden decorator batching and timeouts
- treat batched intents as undelivered on stop
- call
onUndeliveredIntentcorrectly when buffer overflows - cancel coroutines launched in plugins during tests when harness block ends. This fixes "hanging" tests due to plugins launching coroutines.
- allow decorators to define handlers even if children don't specify a hook. this fixes skipped logic in decorators because the plugin didn't define a callback.
- synchronize batched intent flushing
❔ Other
- extend update_deps.sh to autobump docs
- Enhance documentation for updateState function (#169)
- Add Claude Code GitHub Workflow (#175)
- update config
- update deps: kotlin 2.2.21
- promote subscription aware APIs to stable
- add more tests for delegates
- let kotest run on more platforms since 6.0
- trim redundant tests
- expand decorator test coverage
- test coverage for conflate decorators
- validate batch queue flush
- enable return value checker and other flags
- change syntax of setOnce to be infix
- fix deprecations
- gradle 9.2
📚 Docs
add docusaurus-plugin-llms for AI-friendly documentation
-
Any docs page can now be used with an
.mdfile suffix tocurl -sSit to your agent. E.g: https://opensource.respawn.pro/FlowMVI/plugins/delegates.md -
Also published llms.txt and llms-full.txt :
-
clarify batch intents unhandled intent callback
New Contributors
- @dimagor555 made their first contribution in #169
Full Changelog: 3.2.0...3.2.1
3.2.0
3.2.0
🚀 New Features
- Child and Delegated Stores: New plugins
childStorePluginandstoreDelegatePluginallow composing stores into tree-like hierarchies and splitting responsibilities more easily. This enables better code organization by creating parent-child relationships between stores, where child stores can handle specific sub-features while maintaining their own lifecycle. The sample app demonstrates progressive content loading implemented with just 7 lines of code using this feature. Learn more in the delegates documentation. - SubscriptionAware:
PipelineContextnow implementsSubscriptionAwareinterface to observe subscribers from within the store, enabling plugins to react to subscription lifecycle events. This foundation powers the completely reimplementedwhileSubscribedplugin, which now includes a configurable delay before reporting unsubscriptions to avoid wasted resources during configuration changes (e.g., Android screen rotations). This makes resource management more efficient while maintaining responsiveness. - Updated IDE plugin live templates to generate plugins for specific stores
- Custom descriptive exceptions for duplicate store startup
- Added logging convenience functions like logi, logw, loge and more.
🧨 API Changes
- Removed deprecated
parentStoreandsavedStateplugins - Saver API Changes:
recoverfunction deprecated in favor of newRecoveringSaver - Changed file saver path parameters to lazy lambdas (previous API deprecated)
- Removed inline from
CallbackSaverto fix compiler crashes - Migrated to context parameters partially (old receiver functions un-deprecated)
🐞 Bug Fixes
- Fixed no-op saver test throwing
ClassCastExceptionon JVM - Created parent directories when writing files on native
- Fixed saver on Native throwing obscure errors without considering recovery
- Fixed no message when duplicate plugin installed in store
- Fixed clipboard copy not working in debugger
- Added interruption support to blocking file IO calls in save state plugin
- Added missing
DelicateStoreApito state property - Added missing name parameter to init plugin
⚙️ Dependencies
- Kotlin 2.2.20 (minimum supported: 2.0.x)
- Gradle and Compose bumped to release versions
- Compose Multiplatform 1.9.0
- Kotlinx.datetime 0.7.1.
❔ Other
- Android compile SDK updated to 36
- Recreated Android keystore (made sample keystore optional for publishing)
- Updated ConnectScreen UI of the debugger to fix layout issues
- Added Google Play publishing requirements to sample app
- Improved test task output
- Added app store screenshots
- Await startup completion in store test DSL
- Refactored FileAccess to use kotlinx.io when possible
📚 Docs
- Added documentation for delegated stores
- Documented store delegate and child stores code
- Updated documentation of whileSubscribed plugin and composable stores
- Updated docs website title style
Testing
- Added tests for various savers
- Added tests for file write/read functions
- Added tests for child stores and delegates
- Added tests for new whileSubscribed plugin
- Added tests for store lifecycle
Contributors
- @egorikftp made their first contribution
Changes since alpha06:
- added logging convenience functions
- CMP 1.9.0 and Kotlin 2.2.20
3.2.0-alpha06
🐞 Bug Fixes
- change file saver path parameters to lazy lambdas. The previous API for FileSaver has been deprecated. This is an improvement needed because platforms like Android require accessing the file system before they can get the file path for caching. With this change, the lambda is accessed on the background thread.
- fix no op saver test throwing ClassCastException sometimes on jvm. Rare bug that pops up for unknown reasons on Android
- remove inline from CallbackSaver to fix compiler crashes.
- Migrated to context parameters partially, the old receiver functions are un-deprecated now. Please open a ticket if you face any issues due to the usage of context parameters.
Dependencies:
- Kotlin 2.0.20-beta. The minimum supported kotlin version is 2.0.x and above.
- compose 1.9.0-beta
- Kotlinx.datetime 0.7.1. This repo was migrated away from Kotlinx.datetime.Instant.
3.2.0-alpha05
🐞 Bug Fixes
- create parent directories when writing to a file on native
- fix recovering saver calling deprecated recover which throws, not allowing null values
❔ Other
- refactor FileAccess to always use kotlinx.io when possible
- add tests to file write/read functions
3.2.0-alpha04
🐞 Bug Fixes
- fixed Saver on Native is throwing obscure errors without considering recovery
- update deps - Kotlin 2.2-RC2, CMP 1.8.1
❔ Other
- fix typo in sample index.html
- add gunk required for google play publishing to the sample app.
- general code cleanup
- add app store screenshots
3.2.0-alpha03
🚀 New Features
- New plugins:
childStorePluginandstoreDelegatePluginallow you to compose stores into tree-like hierarchies and split responsibilities even more easily. Learn more in the new doc page - Let
PipelineContextimplement new SubscriptionAware interface to allow observing subscribers from within the store - Completely new implementation of
whileSubscribedplugin based on new SubscriptionAware support, that now includes a small delay for reporting unsubscriptions to avoid wasted resources during configuration changes on Android. Tests for the plugin have been expanded. The delay can be configured via a parameter. - implement sample app showcase for the delegated stores feature. It implements a progressive content loading feature with 7 lines of code.
- update IDE plugin live templates to allow generating plugins for a specific store or general store
- Use custom, more descriptive exceptions for duplicate store startup
🧨 API Changes
- remove deprecated
parentStoreandsavedStateplugins - The
recoverfunction is now deprecated inSavers. Instead, a newRecoveringSaveris introduced that does the same thing but works not only in framework code, but when you callsaveandrestoreas well. Due to compiler bugs, signatures of some methods had to change, so it may introduce small source breaking changes.
🐞 Bug Fixes
- add missing name parameter to init plugin
- add missing
DelicateStoreApito state property - await the completion of startup on store test DSL. This should improve test stability.
- add interruption support to blocking file IO calls in save state plugin
- fix no message when duplicate plugin is installed in store. Previously it would throw cryptic "kotlin.Unit"
- fix bogus compiler errors with usages of inline in Saver overloads
- fix clipboard copy not working in the debugger
Dependencies
- Kotlin 2.2-RC
- CMP 1.8.1
❔ Other
- update android compile sdk to 36
- remove and re-create android keystore. Make sample keystore optional for publishing.
- Update ConnectScreen ui, use ImageVector logo (#145)
- bump actions/upload-artifact from 4.6.1 to 4.6.2 (#144)
- add rules for AI
- cleanup code and address deprecations
- add tests for store lifecycle
- update docusaurus
- improve test gradle task output
- tests for child stores and delegates
- write tests for the new whileSubscribed plugin
- update docs website title style
📚 Docs
- add documentation for delegated stores
- document store delegate and child stores code
- update documentation of whileSubscribed plugin and composable stores
New Contributors
- @egorikftp made their first contribution in #145
Full Changelog: 3.2.0-alpha01...3.2.0-alpha03
3.2.0-alpha01
⚙️ Dependencies
- Compose Multiplatform 1.8.0-alpha04. This compose release contains breaking changes, such as dropped support for Kotlin K1, Kotlin <2.0 and androidx libraries changes. Please stay on 3.1 if you're still using compose 1.7.3!
- Kotlin 2.1.20-RC
- Essenty 2.5-beta01
- Ktor 3.1
🚀 New Features
- Add
ContainerViewModelclass that allows grabbing the exact type of the container from the ViewModel. This is more flexible as it allows clients to access public methods of the container class itself, instead of just the Store contract. - add
intentextension functions on Container. This makes sending intents easier when Containers are used - add store name to exception messages for easier debugging. Sometimes the error messages are not helpful because they are thrown outside the store. This change allows to find the store with the problem from the stacktrace
🐞 Bug Fixes
- fix intent DSL extensions accepting 0 intents / actions. This is a binary breaking change, but shouldn't need any source changes. It was made because a call like
store.intent()was possible, sending 0 intents, which is a developer error. - Fix
store.subscribe()overloads usingcollectfor states instead ofcollectLatest. When therenderfunction is slow (e.g. resources, file reads, widgets, services) it could lead to the backpressure buildup from the store. This release changes state updates to be cancelled instead, ensuring you show the latest data. I hope that no one relied on the old non-cancellable behavior, but if you do, consider using aProvideroverload and collect states manually.
❔ Other
- removed deprecated
useStatefunction andStoreConfiguraIontop-level properties. - updated run configurations for contributors
📚 Docs
- Add documentation on DI setup with Kodein and Koin #138 #131. The page contains ready-to-use samples on how to inject containers using DI as ViewModels with lifecycle and backstack support.
- Add resources page with articles, case studies, samples #139
- Update contribution guide with more detailed setup
3.1.0
Highlights of 3.1 release
API Changes:
- Reversed the order of
onStopinvocations. TheonStopcallback in plugins is now invoked in the opposite order (bottom to top)! This change was made to support using and referencing plugins or values of a store (such as cache) in theonStopcallback. Most users shouldn't be affected, but ideally you should check each place whereonStopis used manually. updateStateImmediateis now inline. This will require additional imports in your code.- the unsafe
stateproperty of the store is now inline. Additional imports will be needed in your code if you are using it. - In test DSL, the time travel plugin is now installed
afterthe plugin is run, meaning it gets the resulting value of the plugin test, not the initial. For example, if previously a plugin swallowed an exception, it was still present in theTimeTravel. Now it isn't, to better align with expectations on plugin execution. - Changed the return type of
Store.start()to a new object -StoreLifecycle- which can be used to wait for the store to start, stop, and shut it down.
New features
- First release of the IDE plugin 💫! It provides easy to use Live templates which generate stores, plugins, screens for you using shortcuts
fmvim- for models,fmvic- for containers,fmvis- for composable screens,fmvip- for plugins, and more.- Additionally, the plugin fully includes the debugger, previously packaged as a standalone app.
- New branch of plugin DSL - Decorators! Currently experimental, they are very similar to plugins, but allow to intercept and observe the invocation chain of a plugin, or the entire store. They are a more powerful alternative to plugins with a slightly different behavior. See docs for more info.
- Debugger now allows basic operations to control the store
- New
onStophandler callback forPipelineContext, which will invoke an action on store closure. It is less safe than thedeinitplugin but can be used where deinit is not available, e.g. async code. androidmodule now has more than just the android target and uses the new Androidx.Viewmodel multiplatform support.- Wasm WASI support for
coremodule - New plugin callbacks:
onUndeliveredIntentto handle undelivered intentsonUndeliveredAction- same for actions.onStopnow has access to store config and can change the state.
- Huge performance improvements to the library improve the speed by 250-1600%, making the library one of the fastest among 35 researched alternatives
- New config property
stateStrategythat replaces theatomicStateUpdatesand allows for customization of whether the state transactions will be reentrant, serialized, or immediate. The non-reentrant strategy now includes runtime checks that prevent deadlocks (active in debug mode only) - New config property
allowTransientSubscriptionsallows to control whether the subscribers can leave and return on their own - New
savedStatePlugin'sSaveBehavior-Periodic, that allows to save the state every N seconds. - Quickstart has been rewritten from scratch to explain the library in <10 minutes. Added a page for decorators and updated readme as well
New Plug-Ins
resetStatePlugin- to clean up the store's stateasyncCache- to asynchronously run initialization routines and cache the resultdeinit- for running actions when the store is stopped
New Decorators
intentTimeout- for disallowing long reducing operationsconflateIntents/conflateActions- for preventing duplicate eventsretryIntents- for retrying failed intent reductionbatchIntents- sends intents in batches to improve peformance
Dependencies:
- CMP 1.7.3
- Kotlin 2.0.21
- Coroutines 1.10.1
- Ktor 3.0.3 for debugger
- Serialization 1.8-RC
Bug fixes
- Important fix for #121 which would throw
UnrecoverableExceptionif the exception was caught in a nested clause, like:onStart { updateState { throw Exception() } } - A lot of issues fixed on the debugger
- Improved visuals of the debugger and sample app, new theme colors
Since 3.1.0-beta06 release:
- fix plugin live templates
- fix wide screen display on sample app
- bump deps
- update readme
- remove dokka workaround and enable it back