Revamp compose-routing
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package it.vfsfitvnm.route
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.SaverScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
|
||||
@Stable
|
||||
@Immutable
|
||||
open class Route internal constructor(val tag: String) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when {
|
||||
@@ -19,7 +24,7 @@ open class Route internal constructor(val tag: String) {
|
||||
}
|
||||
|
||||
object GlobalEmitter {
|
||||
var listener: ((Route) -> Unit)? = null
|
||||
var listener: ((Route, Array<Any?>) -> Unit)? = null
|
||||
}
|
||||
|
||||
object Saver : androidx.compose.runtime.saveable.Saver<Route?, String> {
|
||||
@@ -35,10 +40,8 @@ fun rememberRoute(route: Route? = null): MutableState<Route?> {
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class Route0(
|
||||
tag: String
|
||||
) : Route(tag) {
|
||||
@Immutable
|
||||
class Route0(tag: String) : Route(tag) {
|
||||
context(RouteHandlerScope)
|
||||
@Composable
|
||||
operator fun invoke(content: @Composable () -> Unit) {
|
||||
@@ -48,64 +51,36 @@ class Route0(
|
||||
}
|
||||
|
||||
fun global() {
|
||||
GlobalEmitter.listener?.invoke(this)
|
||||
GlobalEmitter.listener?.invoke(this, emptyArray())
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class Route1<P0>(
|
||||
tag: String,
|
||||
state0: MutableState<P0>
|
||||
) : Route(tag) {
|
||||
var p0 by state0
|
||||
|
||||
@Immutable
|
||||
class Route1<P0>(tag: String) : Route(tag) {
|
||||
context(RouteHandlerScope)
|
||||
@Composable
|
||||
operator fun invoke(content: @Composable (P0) -> Unit) {
|
||||
if (this == route) {
|
||||
if (route is Route1<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(route as Route1<P0>).let { route ->
|
||||
this.p0 = route.p0
|
||||
}
|
||||
}
|
||||
content(this.p0)
|
||||
content(parameters[0] as P0)
|
||||
}
|
||||
}
|
||||
|
||||
fun global(p0: P0 = this.p0) {
|
||||
this.p0 = p0
|
||||
GlobalEmitter.listener?.invoke(this)
|
||||
fun global(p0: P0) {
|
||||
GlobalEmitter.listener?.invoke(this, arrayOf(p0))
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class Route2<P0, P1>(
|
||||
tag: String,
|
||||
state0: MutableState<P0>,
|
||||
state1: MutableState<P1>
|
||||
) : Route(tag) {
|
||||
var p0 by state0
|
||||
var p1 by state1
|
||||
|
||||
@Immutable
|
||||
class Route2<P0, P1>(tag: String) : Route(tag) {
|
||||
context(RouteHandlerScope)
|
||||
@Composable
|
||||
operator fun invoke(content: @Composable (P0, P1) -> Unit) {
|
||||
if (this == route) {
|
||||
if (route is Route2<*, *>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(route as Route2<P0, P1>).let { route ->
|
||||
this.p0 = route.p0
|
||||
this.p1 = route.p1
|
||||
}
|
||||
}
|
||||
content(this.p0, this.p1)
|
||||
content(parameters[0] as P0, parameters[1] as P1)
|
||||
}
|
||||
}
|
||||
|
||||
fun global(p0: P0 = this.p0, p1: P1 = this.p1) {
|
||||
this.p0 = p0
|
||||
this.p1 = p1
|
||||
GlobalEmitter.listener?.invoke(this)
|
||||
fun global(p0: P0, p1: P1) {
|
||||
GlobalEmitter.listener?.invoke(this, arrayOf(p0, p1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,12 @@ import androidx.compose.animation.AnimatedContentScope
|
||||
import androidx.compose.animation.ContentTransform
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@@ -45,9 +50,14 @@ fun RouteHandler(
|
||||
) {
|
||||
val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
|
||||
val parameters = rememberSaveable {
|
||||
arrayOfNulls<Any?>(2)
|
||||
}
|
||||
|
||||
val scope = remember(route) {
|
||||
RouteHandlerScope(
|
||||
route = route,
|
||||
parameters = parameters,
|
||||
push = onRouteChanged,
|
||||
pop = { if (handleBackPress) backDispatcher?.onBackPressed() else onRouteChanged(null) }
|
||||
)
|
||||
@@ -55,7 +65,10 @@ fun RouteHandler(
|
||||
|
||||
if (listenToGlobalEmitter) {
|
||||
LaunchedEffect(route) {
|
||||
Route.GlobalEmitter.listener = if (route == null) onRouteChanged else null
|
||||
Route.GlobalEmitter.listener = if (route == null) ({ newRoute, newParameters ->
|
||||
newParameters.forEachIndexed(parameters::set)
|
||||
onRouteChanged(newRoute)
|
||||
}) else null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +78,7 @@ fun RouteHandler(
|
||||
|
||||
updateTransition(targetState = scope, label = null).AnimatedContent(
|
||||
transitionSpec = transitionSpec,
|
||||
contentKey = { it.route?.tag },
|
||||
contentKey = RouteHandlerScope::route,
|
||||
modifier = modifier,
|
||||
) {
|
||||
it.content()
|
||||
|
||||
@@ -2,11 +2,12 @@ package it.vfsfitvnm.route
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
@Immutable
|
||||
@Stable
|
||||
class RouteHandlerScope(
|
||||
val route: Route?,
|
||||
val parameters: Array<Any?>,
|
||||
private val push: (Route?) -> Unit,
|
||||
val pop: () -> Unit,
|
||||
) {
|
||||
@@ -18,23 +19,17 @@ class RouteHandlerScope(
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Route0.invoke() {
|
||||
operator fun Route.invoke() {
|
||||
push(this)
|
||||
}
|
||||
|
||||
operator fun <P0> Route1<P0>.invoke(
|
||||
p0: P0 = this.p0
|
||||
) {
|
||||
this.p0 = p0
|
||||
push(this)
|
||||
operator fun <P0> Route.invoke(p0: P0) {
|
||||
parameters[0] = p0
|
||||
invoke()
|
||||
}
|
||||
|
||||
operator fun <P0, P1> Route2<P0, P1>.invoke(
|
||||
p0: P0 = this.p0,
|
||||
p1: P1 = this.p1
|
||||
) {
|
||||
this.p0 = p0
|
||||
this.p1 = p1
|
||||
push(this)
|
||||
operator fun <P0, P1> Route.invoke(p0: P0, p1: P1) {
|
||||
parameters[1] = p1
|
||||
invoke(p0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package it.vfsfitvnm.route
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.AnimatedContentScope
|
||||
import androidx.compose.animation.ContentTransform
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.tween
|
||||
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.with
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
val AnimatedContentScope<RouteHandlerScope>.leftSlide: ContentTransform
|
||||
|
||||
Reference in New Issue
Block a user