Navigation Compose Typed

Compile-time type-safe arguments for JetPack Navigation Compose library. Based on KotlinX.Serialization.

Kiwi.com library CI Build GitHub release Maven release

Major features:

  • Complex types support, including nullability for primitive types – the only condition is that the type has to be serializable with KotlinX.Serializable library.
  • Based on official Kotlin Serialization compiler plugin – no slowdown with KSP nor KAPT.
  • All JetPack Navigation Compose features: e.g. navigateUp() after a deeplink preserves the top-level shared arguments.
  • Few simple functions, no new complex NavHost or NavController types; this allows covering other JetPack Navigation Compose extensions.
  • Gradual integration, feel free to onboard just a part of your app.

QuickStart

Add the dependency

implementation("com.kiwi.navigation-compose.typed:core:<version>")

Warning This library uses Semantic Versioning. Be aware that BC breaks are allowed in minor versions before the major 1.0 version.

Create app’s destinations

import com.kiwi.navigationcompose.typed.Destination

sealed interface Destinations : Destination {
	@Serializable
	object Home : Destinations

	@Serializable
	data class Article(
		val id: String,
	) : Destinations
}

and use them in the navigation graph definition

import com.kiwi.navigationcompose.typed.composable
import com.kiwi.navigationcompose.typed.createRoutePattern

NavGraph(
	startDestination = createRoutePattern<Destinations.Home>(),
) {
	composable<Destinations.Home> {
		Home()
	}
	composable<Destinations.Article> {
		// this is Destinations.Article
		Article(id)
	}
}

Now, it is time to navigate! Create a Destination instance and pass it to the navigate extension method on the standard NavController.

import com.kiwi.navigationcompose.typed.Destination
import com.kiwi.navigationcompose.typed.navigate

@Composable
fun AppNavHost() {
	val navController = rememberNavController()
	NavGraph(
		navController = navController,
	) {
		composable<Destinations.Home> {
			Home(navController::navigate)
		}
	}
}

@Composable
private fun Home(
	onNavigate: (Destination) -> Unit,
) {
	Home(
		onArticleClick = { id -> onNavigate(Destinations.Article(id)) },
	)
}

@Composable
private fun Home(
	onArticleClick: (id: Int) -> Unit,
) {
	Column {
		Button(onClick = { onArticleClick(1) }) { Text("...") }
		Button(onClick = { onArticleClick(2) }) { Text("...") }
	}
}

Extensibility

What about cooperation with Accompanist’s AnimatedNavHost or bottomSheet {}? Do not worry. Basically, all this are just few simple functions. Create your own abstraction and use createRoutePattern(), createNavArguments(), decodeArguments() and registerDestinationType() functions.

import com.kiwi.navigationcompose.typed.createRoutePattern
import com.kiwi.navigationcompose.typed.createNavArguments
import com.kiwi.navigationcompose.typed.decodeArguments
import com.kiwi.navigationcompose.typed.Destination
import com.kiwi.navigationcompose.typed.registerDestinationType

private inline fun <reified T : Destination> NavGraphBuilder.bottomSheet(
	noinline content: @Composable T.(NavBackStackEntry) -> Unit,
) {
	val serializer = serializer<T>()
	registerDestinationType(T::class, serializer)
	bottomSheet(
		route = createRoutePattern(serializer),
		arguments = createNavArguments(serializer),
	) {
		val arguments = decodeArguments(serializer, it)
		arguments.content(it)
	}
}

NavGraph {
	bottomSheet<Destinations.Article> {
		Article(id)
	}
}

GitHub

View Github