Compose-multiplatform navigation library leveraging Decompose to manage screen configurations with a `Router`, supporting retention of view models across configuration changes and multiple platforms.
Copyright 2024 Isuru Rajapakse
Licensed under the Apache License, Version 2.0 (the "License");
you may not use thisfile except in compliance the License.
You may obtain a copy of the License at
http:
Unless applicable law agreed to writing, software
distributed under the License distributed an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
See the License the specific language governing permissions
limitations under the License.
// Declare your screen configurations as @Serializable for type-safety@SerializablesealedclassScreen : Parcelable {
dataobject List : Screen()
dataclassDetails(val detail: String) : Screen()
}
@ComposablefunListDetailScreen() {
// Create a router with a stack of screen configurations 🚏val router: Router<Screen> = rememberRouter { listOf(List) }
// Hoist your screens for each configuration 🏗️
RoutedContent(router = router) { screen ->
when (screen) {
List -> ListScreen(
// Navigate by pushing new configurations on the router 🧭
onSelectItem = { detail -> router.push(detail) }
)
is Details -> DetailsScreen(screen.detail)
}
}
}
@ComposablefunDetailsScreen(detail: String) {
// 📦 Scope an instance (a view model, a state-holder or whatever) to a route with [rememberOnRoute] // This makes your instances survive configuration changes (on android) 🔁// And holds-on the instance as long as it is in the backstack 🔗// Pass in key if you want to reissue a new instance when key changes 🔑 (optional) val viewModel: DetailViewModel = rememberOnRoute(key = detail) { // this: RouterContext
DetailViewModel(this, detail)
// Optional, if you want your coroutine scope to be cancelled when the screen is removed from the backstack
.apply { doOnDestroy { cancel() } }
}
val state: DetailState by viewModel.states.collectAsState()
Text(text = state.detail)
}
classDetailViewModel(context: RouterContext, detail: String): CoroutineScope {
// Optional, if you want to scope your coroutines to the lifecycle of this screenoverrideval coroutineContext: CoroutineContext = Dispatchers.Main + SupervisorJob()
// Optional, if you want your state to survive process death ☠️ // Derive your initial state from [RouterContext.state] privateval initialState: DetailState = context.state(DetailState(detail)) { states.value }
privateval stateFlow = MutableStateFlow(initialState)
val states: StateFlow<DetailState> = stateFlow
}
with
//www.apache.org/licenses/LICENSE-2.0
required
by
or
in
is
on
"AS IS"
or
for
and
Related libraries
Surfaced from shared tags and platforms — no rankings paid for.