Releases: ikarenkov/Modo
v0.11.0
New Features
ProvideOverlayIntegration โ window overlay support (#72)
New composable extension on Screen that properly integrates window-based overlays (Dialog, ModalBottomSheet, Popup, etc.) with Modo's screen lifecycle and state management.
Problem it solves: Window-based overlays create a separate AndroidComposeView with composition locals inherited from Activity level, not from the Screen. This caused:
ViewModelscoped to Activity instead of ScreenrememberSaveablestate lost on back navigation
Usage:
ModalBottomSheet(onDismissRequest = { navigation.back() }) {
ProvideOverlayIntegration {
MyBottomSheetContent()
}
}Bug Fixes
- Fixed crash when several permanent dialogs were forwarded with a screen, causing wrong lifecycle events order (#75)
- Fixed
ScreenModelStoreclearing wrong screens due to key prefix collision โ screens with similar key prefixes were incorrectly cleaned up (#73) - Fixed custom dialogs transition animation that was broken in certain cases (#71)
- Fixed recompositions in sample caused by counter (#75)
Deprecations
Modo.getOrCreateRootScreenโ useModo.rememberRootScreeninstead (#76)Modo.saveโ useModo.rememberRootScreeninstead (#76)Modo.onRootScreenFinishedโ useModo.rememberRootScreeninstead (#76)
All deprecated APIs will be removed in 1.0.
API Changes
SaveableContent โ removed manualResumePause parameter (#75)
The manualResumePause: Boolean parameter was removed from SaveableContent; the transition lifecycle context is now propagated via an internal CompositionLocal. If you were passing this parameter explicitly, remove it from your call sites.
Refactoring
ScreenLifecycleManagerโ lifecycle management logic extracted fromModoScreenAndroidAdapterinto a dedicated class, making lifecycle handling more predictable and testable (#71, #75)
Dependency Updates
| Dependency | Before | After |
|---|---|---|
| Kotlin | 1.9.23 |
2.0.21 |
| AGP | 8.4.0 |
8.13.2 |
| Compose BOM | 2024.03.00 |
2025.11.01 |
compileSdk |
34 |
36 |
Improvements
- Extensive new tests for
ModoScreenAndroidAdapterandScreenModelStore(#71, #73, #75, #76) - Added dependency guard plugin to track and control library's public dependencies (#69)
- Migrated publishing to Sonatype Central Portal (#74)
What's Changed
- Added dependency guard plugin to control library dependencies by @ikarenkov in #69
- Updated gradle, agp, kotlin by @ikarenkov in #70
- Refactored screens cleanup and lifecycle logic by @ikarenkov in #71
- Add integration for window-based overlays by @ikarenkov in #72
- Fix screen model store clear by @ikarenkov in #73
- Publishing update by @ikarenkov in #74
- Lifecycle refactoring by @ikarenkov in #75
- Refactor Modo init: deprecate legacy API, unify caching, added unit tests by @ikarenkov in #76
Full Changelog: v0.10.1...v0.11.0
v0.10.1
Fixed and documented lifecycle propagation logic
Fixed logic of propagation resume event to child.
- Now you can safely display system dialog and go back, screens will be moved to resume state, when before they stated in started state
- When child is ready to be resumed, but parent is only in started state, child will be resumed as soon as parent moved to resumed state
- Introduced documentation about Lifecycle
What's Changed
- Releases/0.10.0 by @ikarenkov in #64
- Fixed resumed child when parent is not resumed by @ikarenkov in #65
Full Changelog: v0.10.0...v0.10.1
v0.10.0
Lifecycle Improvements and Workshop Updates
In this release, we've focused on fixing bugs and enhancing lifecycle management.
Animation and Screen Lifecycle
The major update: the resumed state now represents a screen that is fully displayed after all animations have completed.
Previous Behavior
When navigating with animations using ScreenTransition, ON_START and ON_RESUME events were triggered as soon as a screen's content became visible. Similarly, ON_PAUSE and ON_STOP were triggered as soon as the screen's content disappeared from the UI.
New Behavior
Weโve introduced a way to detect when animations start and finish for each screen via ON_RESUME and ON_PAUSE events:
ON_STARTandON_STOPretain their previous behavior.ON_RESUMEis now called when the appearance animation finishes.ON_PAUSEis now called when the disappearance animation begins.
What's Changed
- Improved lifecycle support: You can now use
DisposableEffectto observe the lifecycle by @ikarenkov in #56 - Fixed the order of start events by @ikarenkov in #58
- Added
ON_RESUMEandON_PAUSEevents for transitions by @ikarenkov in #59 - Workshop setup by @ikarenkov in #61
- [BUG-60] Added
preDisposeafter state update by @ikarenkov in #62 - Workshop improvements by @ikarenkov in #63
Full Changelog: Compare v0.9.0...v0.10.0
v0.10.0-alpha2
What's Changed
- Fixed order of start events by @ikarenkov in #58
Full Changelog: v0.10.0-alpha1...v0.10.0-alpha2
v0.10.0-alpha1
What's Changed
- Fixed Lifecycle support: now you can use DisposableEffect to observe lifecycle by @ikarenkov in #56
Full Changelog: v0.9.0...v0.10.0-alpha1
v0.9.0
๐ซ Big update with stability and flexibility improvements
๐ฒ Dialogs
Custom dialogs
We introduced the ability to create custom dialogs without using the Dialog composable. This feature adds the dialog's content over the non-dialog content of your StackScreen. You can do so by providing the configuration in your dialog:
class SampleDialog(
private val systemDialog: Boolean = true,
override val screenKey: ScreenKey = generateScreenKey()
) : DialogScreen {
override fun provideDialogConfig(): DialogScreen.DialogConfig = if (systemDialog) {
DialogScreen.DialogConfig.System(
useSystemDim = true,
dialogProperties = DialogProperties(
usePlatformDefaultWidth = true,
decorFitsSystemWindows = true
)
)
} else {
DialogScreen.DialogConfig.Custom
}
}Permanent dialogs
You can mark a dialog as permanent to prevent it from hiding when a new dialog appears on top of it:
class SamplePermanentDialog(
override val screenKey: ScreenKey = generateScreenKey()
) : DialogScreen {
override val permanentDialog: Boolean get() = true
...
}DialogPlaceHolder
We introduced the provideDialogPlaceholderScreen function for StackScreen to provide animations for appearing dialogs. By default, it is just an empty full-screen composable.
PRs
- Ability to use custom dialogs without a system wrapper by @etaskaevhh in #33
- Dialog improvements by @ikarenkov in #36
๐ State modification
Enhanced and simplified state modification!
NavigationAction<NavigationState>- now action is defined byNavigationState, distinguishing actions for different ContainerScreens.ContainerScreen<NavigationState, NavigationAction>- nowContainerScreenis also defined byNavigationAction.- Custom actions - now you can create custom navigation actions in-place and define what they do without creating a custom reducer. To do so, use
ReducerAction<NavigationState>:internal sealed interface RemovableItemContainerAction : ReducerAction<RemovableItemContainerState> { data object Remove : RemovableItemContainerAction { override fun reduce(oldState: RemovableItemContainerState): RemovableItemContainerState = oldState.copy(screen3 = null) } data object CreateScreen : RemovableItemContainerAction { override fun reduce(oldState: RemovableItemContainerState): RemovableItemContainerState = oldState.copy(screen3 = NestedScreen(canBeRemoved = true)) } }
PRs
- Reducer and NavigationAction improvements by @ikarenkov in #37
- Improved container screens implementation by @ikarenkov in #39
- Added clear stack action by @ikarenkov in #48
๐ Samples and Docs
We are happy to introduce our website with documentation!
We welcome any feedback and contributions! You can easily edit pages by clicking "edit" at the top of the page.
PRs
- Enhanced samples, added screen effects samples by @ikarenkov in #38
- Enhanced README and added a list of features and fixed sample package by @ikarenkov in #43
- Docs site setup by @ikarenkov in #49
- Improved existing topics and added new ones, added GitHub link, and changed structure by @ikarenkov in #50
- Updated version and documentation by @ikarenkov in #51
- Docs improvements by @ikarenkov in #52
๐ฃ๏ธ Integrations
Activity and Fragment
We introduced rememberRootScreen for Activity and Fragment, providing a convenient single-line integration of Modo. Documentation.
- Modo integration improvements by @ikarenkov in #40
LazyList utils
We introduced screenItems and screenItem for convenient integration of your screens into LazyList (and pagers).
- Added utils for easy LazyList integration by @ikarenkov in #45
Stability for Screen, ContainerScreen, and NavigationContainer
- Marked Screen, ContainerScreen, and NavigationContainer as stable by @ikarenkov in #46
ListNavigation
If you are seeking for list-based custom navigations, here are some convenient actions for state modification and some inheritance improvements.
- List navigation improvements by @ikarenkov in #55
Bug fixes
Now Modo.init and Modo.getOrCreateRootScreen return the same instance of the RootScreen within a single process. Previously, a new instance was created, leading to problems if you injected RootScreen or any of its children into a DI container.
- New Modo integration fix by @ikarenkov in #44
- Fixed crush for replace command. Covered with tests. Introduced including param for BackTo action. by @ikarenkov in #54
๐ฆฆ Other
- Static analysis setup by @ikarenkov in #41
- Rolled back Android versions by @ikarenkov in #42
โ ๏ธ Migrations and deprecations
Modo.init- renamed and deprecated to prevent confusion. It has been renamed toModo.getOrCreateRootScreento explicitly declare its behavior.Screen.OnScreenRemoved- moved to another package, and the function from the old package is deprecated.com.github.terrakok.modo.model.OnScreenRemoved->com.github.terrakok.modo.lifecycle.OnScreenRemoved
๐ฏโโ๏ธ New Contributors
- @etaskaevhh made their first contribution in #33
Full Changelog: v0.8.0...v0.9.0
v0.9.0-rc1
๐ซ Big update with stability and flexibility improvements
๐ฒ Dialogs
Custom dialogs
We introduced the ability to create custom dialogs without using the Dialog composable. This feature adds the dialog's content over the non-dialog content of your StackScreen. You can do so by providing the configuration in your dialog:
class SampleDialog(
private val systemDialog: Boolean = true,
override val screenKey: ScreenKey = generateScreenKey()
) : DialogScreen {
override fun provideDialogConfig(): DialogScreen.DialogConfig = if (systemDialog) {
DialogScreen.DialogConfig.System(
useSystemDim = true,
dialogProperties = DialogProperties(
usePlatformDefaultWidth = true,
decorFitsSystemWindows = true
)
)
} else {
DialogScreen.DialogConfig.Custom
}
}Permanent dialogs
You can mark a dialog as permanent to prevent it from hiding when a new dialog appears on top of it:
class SamplePermanentDialog(
override val screenKey: ScreenKey = generateScreenKey()
) : DialogScreen {
override val permanentDialog: Boolean get() = true
...
}DialogPlaceHolder
We introduced the provideDialogPlaceholderScreen function for StackScreen to provide animations for appearing dialogs. By default, it is just an empty full-screen composable.
PRs
- Ability to use custom dialogs without a system wrapper by @etaskaevhh in #33
- Dialog improvements by @ikarenkov in #36
๐ State modification
Enhanced and simplified state modification!
NavigationAction<NavigationState>- now action is defined byNavigationState, distinguishing actions for different ContainerScreens.ContainerScreen<NavigationState, NavigationAction>- nowContainerScreenis also defined byNavigationAction.- Custom actions - now you can create custom navigation actions in-place and define what they do without creating a custom reducer. To do so, use
ReducerAction<NavigationState>:internal sealed interface RemovableItemContainerAction : ReducerAction<RemovableItemContainerState> { data object Remove : RemovableItemContainerAction { override fun reduce(oldState: RemovableItemContainerState): RemovableItemContainerState = oldState.copy(screen3 = null) } data object CreateScreen : RemovableItemContainerAction { override fun reduce(oldState: RemovableItemContainerState): RemovableItemContainerState = oldState.copy(screen3 = NestedScreen(canBeRemoved = true)) } }
PRs
- Reducer and NavigationAction improvements by @ikarenkov in #37
- Improved container screens implementation by @ikarenkov in #39
- Added clear stack action by @ikarenkov in #48
๐ Samples and Docs
We are happy to introduce our website with documentation!
We welcome any feedback and contributions! You can easily edit pages by clicking "edit" at the top of the page.
PRs
- Enhanced samples, added screen effects samples by @ikarenkov in #38
- Enhanced README and added a list of features and fixed sample package by @ikarenkov in #43
- Docs site setup by @ikarenkov in #49
- Improved existing topics and added new ones, added GitHub link, and changed structure by @ikarenkov in #50
- Updated version and documentation by @ikarenkov in #51
๐ฃ๏ธ Integrations
Activity and Fragment
We introduced rememberRootScreen for Activity and Fragment, providing a convenient single-line integration of Modo. Documentation.
- Modo integration improvements by @ikarenkov in #40
LazyList utils
We introduced screenItems and screenItem for convenient integration of your screens into LazyList (and pagers).
- Added utils for easy LazyList integration by @ikarenkov in #45
Stability for Screen, ContainerScreen, and NavigationContainer
- Marked Screen, ContainerScreen, and NavigationContainer as stable by @ikarenkov in #46
Bug fixes
Now Modo.init and Modo.getOrCreateRootScreen return the same instance of the RootScreen within a single process. Previously, a new instance was created, leading to problems if you injected RootScreen or any of its children into a DI container.
- New Modo integration fix by @ikarenkov in #44
๐ฆฆ Other
- Static analysis setup by @ikarenkov in #41
- Rolled back Android versions by @ikarenkov in #42
โ ๏ธ Migrations and deprecations
Modo.init- renamed and deprecated to prevent confusion. It has been renamed toModo.getOrCreateRootScreento explicitly declare its behavior.Screen.OnScreenRemoved- moved to another package, and the function from the old package is deprecated.com.github.terrakok.modo.model.OnScreenRemoved->com.github.terrakok.modo.lifecycle.OnScreenRemoved
๐ฏโโ๏ธ New Contributors
- @etaskaevhh made their first contribution in #33
Full Changelog: v0.8.0...v0.9.0-rc1
0.8.0 - Android support and animation bugfixes
Main changes
Android ViewModel and Lifecycle support!
We now have almost full support of Android ViewModel and Lifecycle, check out #35 for detailed diff! Changes:
- Introduced DisposableScreenEffect and LaunchedScreenEffect as an analogue of compose Effects, but linked with screen lifecycle.
- Fixed screen dependencies clear order, now it is FIFO.
- Upgraded androidxLifecycle to 2.7.0
- Fixed logic of clearing during the exit animation - now it clears on animation finish
- Supported Android ViewModelStoreOwner, LifecycleOwner.
- Added ModoDevOptions and assertion to prevent access to ScreenModelStore after screen was removed
Autogen: What's Changedx
- stored codestyle in project by @ikarenkov in #27
- check MODO_GRAPH for null by @trdelnk in #29
- Screen lifecycle OnScreenRemoved callback by @ikarenkov in #28
- Fixed Dialog state and wrong Composition Local providing by @ikarenkov in #30
- Add sample for bottom sheet and changed dialog Api by @ikarenkov in #31
- Update gradle, agp, uses varion catalog and build-logic by @ikarenkov in #34
- First version of the android integration for the modo by @ikarenkov in #35
New Contributors
Full Changelog: v0.7.1...v0.8.0
0.7.1
0.7.0
Modo 0.7.0 for Compose is available ๐
๐ซฃBreaking changes
๐ There is no support for Fragment anymore ๐
Fragments support would take lot of time and was a barrier for Compose development. Also there is no developer who can support modo-fragment. If you are interested in modo-fragment development write to the TG Chat
๐งญ Modo compose
- New project artifact
com.github.terrakok:modo-compose:${latest_version}" - Api changes
ContainerScreenandScreen- main classes to build your own navigation. There are several build in implementations ofContainerScreenlikeStackScreenandMultiScreen- To integrate you screens structure to fragment you simply should use Modo Screen. Usually you just need
StackScreenas root of your hierarchy. - There is no
Modoobject for holding state anymore. State of Modo is just Container screen. Your are allowed to have as many RootScreens as you want simultaneously. ComposeRendererhas become internal.
- (Experimental) Dialog support! You can declare screen as dialog and simple put it in screens structure. There is know bug, when you pop back stack to dialog, then it loses it's state.
- Screen Model Support. You can use
rememberScreenModelinside Composable fun to use screen model connected with Screen. ComposeRendererScopeprovides old and new state to let you create your own Wonderfull animations without limitations to stack state.