Compose Multiplatform Navigation - Pragmatic, type safety navigation for Compose Multiplatform. Based on Freeletics Navigation.
-
Integrate with
Jetpack Compose
andCompose Multiplatform
seamlessly. -
Integrate with kmp-viewmodel library seamlessly
- Stack entry scoped
ViewModel
, exists as long as the stack entry is on the navigation stack, including the configuration changes onAndroid
. - Supports
SavedStateHandle
, used to save and restore data over configuration changes or process death onAndroid
.
- Stack entry scoped
-
Type safety navigation, easy to pass data between destinations.
-
Supports Multi-Backstacks, this is most commonly used in apps that use bottom navigation to separate the back stack of each tab. See Freeletics Navigation - Multiple back stacks.
-
Supports
Lifecycle
events, similar toAndroidX Lifecycle
library.
-
Most of code in
solivagant-khonshu-navigation-core
andsolivagant-navigation
libraries is taken from Freeletics Navigation, and ported toKotlin Multiplatform
andCompose Multiplatform
. -
The
solivagant-lifecycle
library is inspired by Essenty Lifecycle, and AndroidX Lifecycle.
Author: Petrus Nguyễn Thái Học
Liked some of my work? Buy me a coffee (or more likely a beer)
0.x release docs: https://hoc081098.github.io/solivagant/docs/0.x
Snapshot docs: https://hoc081098.github.io/solivagant/docs/latest
allprojects {
repositories {
[...]
mavenCentral()
}
}
implementation("io.github.hoc081098:solivagant-navigation:0.0.1-alpha01")
- The concept is similar to
Freeletics Navigation
library, so you can read the Freeletics Navigation to understand the concept.
@Immutable
@Parcelize
data object StartScreenRoute : NavRoute, NavRoot
@Immutable
@Parcelize
data object SearchProductScreenRoute : NavRoute
@JvmField
val StartScreenDestination: NavDestination =
ScreenDestination<StartScreenRoute> { StartScreen() }
@Composable
internal fun StartScreen(
modifier: Modifier = Modifier,
viewModel: StartViewModel = koinKmpViewModel(),
) {
// UI Composable
}
class StartViewModel(
// used to trigger navigation actions from outside the view layer (e.g. from a ViewModel).
// Usually, it is singleton object, or the host Activity retained scope.
private val navigator: NavEventNavigator,
) : ViewModel() {
internal fun navigateToProductsScreen() = navigator.navigateTo(ProductsScreenRoute)
internal fun navigateToSearchProductScreen() = navigator.navigateTo(SearchProductScreenRoute)
}
@JvmField
val SearchProductScreenDestination: NavDestination =
ScreenDestination<SearchProductScreenRoute> { SearchProductsScreen() }
@Composable
fun SearchProductsScreen(
modifier: Modifier = Modifier,
viewModel: SearchProductsViewModel = koinKmpViewModel<SearchProductsViewModel>(),
) {
// UI Composable
}
class SearchProductsViewModel(
private val searchProducts: SearchProducts,
private val savedStateHandle: SavedStateHandle,
// used to trigger navigation actions from outside the view layer (e.g. from a ViewModel).
// Usually, it is singleton object, or the host Activity retained scope.
private val navigator: NavEventNavigator,
) : ViewModel() {
fun navigateToProductDetail(id: Int) {
navigator.navigateTo(ProductDetailScreenRoute(id))
}
}
@Stable
private val AllDestinations: ImmutableSet<NavDestination> = persistentSetOf(
StartScreenDestination,
SearchProductScreenDestination,
// and more ...
)
@Composable
fun MyAwesomeApp(
// used to trigger navigation actions from outside the view layer (e.g. from a ViewModel).
// Usually, it is singleton object, or the host Activity retained scope.
navigator: NavEventNavigator,
modifier: Modifier = Modifier,
) {
var currentRoute: BaseRoute? by remember { mutableStateOf(null) }
NavHost(
modifier = modifier,
// route to the screen that should be shown initially
startRoute = StartScreenRoute,
// should contain all destinations that can be navigated to
destinations = AllDestinations,
navEventNavigator = navigator,
destinationChangedCallback = { currentRoute = it },
)
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate()
// navigator can be retrieved from the DI container, such as Koin, Dagger Hilt, etc.
setContent {
MyAwesomeApp(
navigator = navigator
)
}
}
}
// navigate to the destination that the given route leads to
navigator.navigateTo(DetailScreenRoute("some-id"))
// navigate up in the hierarchy
navigator.navigateUp()
// navigate to the previous destination in the backstack
navigator.navigateBack()
// navigate back to the destination belonging to the referenced route and remove all destinations
// in between from the back stack, depending on inclusive the destination
navigator.navigateBackTo<MainScreenRoute>(inclusive = false)
- Samples sample: a complete sample using
Compose Multiplatform (Android, Desktop, iOS)
solivagant-navigation
for navigation in Compose Multiplatform.kmp-viewmodel
to shareViewModel
andSavedStateHandle
.Koin DI
.
- Add more tests
- Support transition when navigating
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/