Strongly Typed APIs - Compose Navigation Transitions
A nice detail I’ve noticed in the Compose Navigation library is the usage of strong types.
Let’s take a look at the NavHost composable API.
@Composable
fun NavHost(
navController: NavHostController,
graph: NavGraph,
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
fadeIn(animationSpec = tween(700))
},
exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
fadeOut(animationSpec = tween(700))
},
popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = enterTransition,
popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = exitTransition,
sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)? = null
): Unit
The definition of enterTransition
is fantastic in its simplicity. Its receiver and return type are doing a remarkable amount of heavy lifting. I’m interested
enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
Its return type, EnterTransition, prevents a user from adding a new NavBackStackEntry and using fadeOut to add it to the screen. The code enterTransition = { fadeOut(/* args */ }
cannot compile, preventing a category of runtime errors. A far cry from the days of @AnimRes or Animation objects, types that lack the semantic simplicity and richness of
AnimatedContentTransitionScope<*> as a Receiver is also nice. Any functions added are a receiver-scope extension; only accessible in functions with this as a receiver.
These are small details, but they make these APIs incredibly accessible and straightforward to use.