Skip to content

Instantly share code, notes, and snippets.

@axiel7
Last active November 5, 2023 13:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save axiel7/2c51ceb5ec8d6229cee001c7c28bb2dd to your computer and use it in GitHub Desktop.
Save axiel7/2c51ceb5ec8d6229cee001c7c28bb2dd to your computer and use it in GitHub Desktop.
Simple Compose Navigation Wrapper
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavType
import androidx.navigation.navArgument
enum class ArgumentType {
String, Int, Boolean, BooleanOptional;
fun toNavType() = when (this) {
String -> NavType.StringType
Int -> NavType.IntType
Boolean -> NavType.BoolType
BooleanOptional -> NavType.StringType // null boolean is not supported in androidx.navigation
}
val isOptional
get() = when (this) {
BooleanOptional -> true
else -> false
}
}
// Argument names
enum class NavArgument(
val type: ArgumentType,
) {
ExampleId(type = ArgumentType.Int),
// put all the arguments used in you app here
}
// The actual argument used in navigation
data class DestArgument(
val argument: NavArgument,
val isNullable: Boolean = false,
val defaultValue: Any? = null,
) {
// Converts to androidx.navigation argument
fun toNamedNavArgument() = navArgument(argument.name) {
type = argument.type.toNavType()
nullable = isNullable || argument.type.isOptional
if (this@DestArgument.defaultValue != null) {
defaultValue = this@DestArgument.defaultValue
}
}
companion object {
fun NavBackStackEntry.getStringArg(destArgument: DestArgument?) =
if (destArgument?.argument?.type == ArgumentType.String)
arguments?.getString(
destArgument.argument.name,
destArgument.defaultValue as? String?
)
else null
fun NavBackStackEntry.getIntArg(destArgument: DestArgument?) =
if (destArgument?.argument?.type == ArgumentType.Int)
if (destArgument.defaultValue is Int)
arguments?.getInt(destArgument.argument.name, destArgument.defaultValue)
else arguments?.getInt(destArgument.argument.name)
else null
fun NavBackStackEntry.getBoolean(destArgument: DestArgument?) =
if (destArgument?.argument?.type == ArgumentType.Boolean) {
if (destArgument.defaultValue is Boolean)
arguments?.getBoolean(destArgument.argument.name, destArgument.defaultValue)
else arguments?.getBoolean(destArgument.argument.name)
} else if (destArgument?.argument?.type == ArgumentType.BooleanOptional) {
if (destArgument.defaultValue is Boolean) {
arguments?.getString(
destArgument.argument.name,
destArgument.defaultValue.toString()
)?.toBooleanStrictOrNull()
} else arguments?.getString(destArgument.argument.name)?.toBooleanStrictOrNull()
} else null
}
}
// Put all the destinations here
enum class NavDestination(
private val arguments: List<DestArgument> = emptyList()
) {
ExampleDestination(
arguments = listOf(
DestArgument(
argument = NavArgument.ExampleId
)
)
),
;
fun findDestArgument(argument: NavArgument) = arguments.find { it.argument == argument }
fun route() = if (arguments.isEmpty()) name else {
name + arguments
.sortedBy { it.isNullable }
.joinToString(separator = "") { arg ->
if (arg.isNullable) "?${arg.argument.name}={${arg.argument.name}}"
else "/{${arg.argument.name}}"
}
}
val namedNavArguments get() = arguments.map { it.toNamedNavArgument() }
fun putArguments(arguments: Map<NavArgument, String?>): String {
var routeWithArguments = route()
arguments.forEach { (arg, value) ->
if (value != null)
routeWithArguments = routeWithArguments.replace("{${arg.name}}", value)
}
return routeWithArguments
}
}
@axiel7
Copy link
Author

axiel7 commented Nov 5, 2023

Usage

NavHost(
    ...
) {
    composable(
        route = NavDestination.ExampleDestination.route(),
        arguments = NavDestination.ExampleDestination.namedNavArguments
    ) { navEntry ->
        ExampleScreen(
            exampleId = navEntry.getIntArg(
                NavDestination.ExampleDestination.findDestArgument(NavArgument.ExampleId)
            )
        )
    }
}

// Navigate
navController.navigate(
    NavDestination.ExampleDestination
        .putArguments(mapOf(NavArgument.ExampleId to id.toString()))
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment