Compose Destinations - Navigation Library
Tags: android, beginners, kotlin, jetpackcompose
Published: 2022-05-21 00:51:53

**How to convert your Jetpack Compose navigation app to use Compose Destinations Library to get rid of boilerplate code?**

This is part of the Jetpack Compose navigation series:

* [Part 1 - Simple Jetpack Compose Navigation Example](https://vtsen.hashnode.dev/simple-jetpack-compose-navigation-example)
    
* **Part 2 - Compose Destinations - Navigation Library**
    
* [Part 3 - How to Add Bottom Navigation in Jetpack Compose?](https://vtsen.hashnode.dev/how-to-add-bottom-navigation-in-jetpack-compose)
    
* [Part 4 - How to Add Navigation Drawer in Jetpack Compose?](https://vtsen.hashnode.dev/how-to-add-navigation-drawer-in-jetpack-compose)
    
* [Part 5 - How to Add Deep Links in Jetpack Compose?](https://vtsen.hashnode.dev/how-to-add-deep-links-in-jetpack-compose)

In my previous post, I build a very simple Jetpack Compose Navigation and use the [NavRoute](https://vtsen.hashnode.dev/simple-jetpack-compose-navigation-example#heading-too-much-boilerplate-code) sealed class to avoid hard coding strings in multiple places. 

However, a better solution may be just using this awesome [Compose Destinations](https://github.com/raamcosta/compose-destinations) library! Let's see how we can convert this app to use this library.

## Setup build.gradle (module level)

### 1. Add KSP Plugin

Add `com.google.devtools.ksp` plugin
```
plugins {
    ...
    id 'com.google.devtools.ksp' version '1.6.10-1.0.2'
}
```

### 2. Add Generated KSP Path
Add generated KSP path inside the `android` block

```
android {
    ...
    applicationVariants.all { variant ->
        kotlin.sourceSets {
            getByName(variant.name) {
                kotlin.srcDir("build/generated/ksp/${variant.name}/kotlin")
            }
        }
    }
}

```

### 3. Add Compose Destination Dependencies
```
dependencies {
    ...
    implementation 'io.github.raamcosta.compose-destinations:core:1.5.1-beta'
    ksp 'io.github.raamcosta.compose-destinations:ksp:1.5.1-beta'   
} 
```

> For `build.gradle.kts` example, you can refer to this commit [here](https://github.com/vinchamp77/YoutubeWorkout/commit/49792285d9daaced2d32f77b1ba22bbaea2d766f).

## Build Navigation Graph
Existing navigation graph related code (i.e. `BuildNavGraph()` and `NavRoute`) code can be removed completely and replaced with compose destinations annotations.

### 1. Annotate Screens with `@Destination` 

Annotate all composable screens with `@Destination`
```kotlin
@Destination
@Composable
fun LoginScreen(
   ...
) {
...

@Destination
@Composable
fun HomeScreen(
   ...
) {
...
@Destination
@Composable
fun ProfileScreen(
   ...
) {
...
@Destination
@Composable
fun SearchScreen(
   ...
) {
...
```

### 2. Annotate Start Screen with `@RootNavGraph(start = true)`

```kotlin
@RootNavGraph(start = true)
@Destination
@Composable
fun LoginScreen(
   ...
) 
...
```
> After you annotate the composable screen, make sure you **Rebuild Project** so all the necessary generated code will be generated.

### 3. Replace `NavHostController` with `DestinationsNavigator`

In the original login composable screen, it has this `navigateToHome` callback.

```kotlin
fun LoginScreen(
    navigateToHome: () -> Unit
) {
    ...
}
```
Now, it can be replaced with `DestinationsNavigator` parameter.
```kotlin
fun LoginScreen(
    navigator: DestinationsNavigator
) {
   ...
}
```
To navigate, the original implementation use `NavHostController`

```kotlin
navController.navigate(NavRoute.Home.path)
```
and now is replaced with `DestinationsNavigator` 
```kotlin
navigator.navigate(HomeScreenDestination)
```
> `HomeScreenDestination` is the generated code.

Some other conversion examples below
```kotlin
// #1  - popBackStack() 
// convert NavHostController
navController.popBackStack()
// to DestinationsNavigator
navigator.popBackStack()

// #2 - navigate with arguments
// convert NavHostController
navController.navigate(NavRoute.Profile.withArgs(id.toString(), showDetails.toString()))
// to DestinationsNavigator
navigator.navigate(ProfileScreenDestination(7, true))

// #3  - popUpTo() 
// convert NavHostController
navController.navigate(NavRoute.Login.path) {
        popUpTo(NavRoute.Login.path) {inclusive = true}
}
// to DestinationsNavigator
navigator.navigate(LoginScreenDestination) {
   popUpTo(LoginScreenDestination.route) {inclusive = true}
}
```
> As you can see, the `DestinationsNavigator` is basically a wrapper for `NavHostController` which makes it a lot easier.

### 4. Call `DestinationsNavHost()` in the main composable screen

Replace `BuildNavGraph()` 
```kotlin
@Composable
private fun MainScreen() {
    SimpleNavComposeAppTheme {
        val navController = rememberNavController()
        BuildNavGraph(navController)
    }
}
```

with `DestinationsNavHost()`

```kotlin
@Composable
private fun MainScreen() {
    SimpleNavComposeAppTheme {
        DestinationsNavHost(navGraph = NavGraphs.root)
    }
}
```

### 5. Use `EmptyDestinationsNavigator` in `@Preview`

Thanks to the author of this library, Rafael Costa told me that I can actually use `EmptyDestinationsNavigator` as null implementation and used it for `@preview` instead of passing in`null`. 

Instead of passing in `navigator = null`, I can pass in `navigator = EmptyDestinationsNavigator`.

```kotlin
@Preview(showBackground = true)
@Composable
private fun DefaultPreview() {
    SimpleNavComposeAppTheme(useSystemUiController = false) {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colors.background
        ) {     
            HomeScreen(navigator = EmptyDestinationsNavigator)
       }
    }
}
```

By doing that, I don't need to declare `navigator: DestinationsNavigator?` nullable variable parameter in the composable function.

Done!


## Conclusion
This library is awesome! It gets rid of much boilerplate code. One thing I wish is I don't need to set up as in **Step 1 - Add KSP Plugin** and **Step 2 - Add Generated KSP Path** above, but maybe that is not technically feasible.

## Source Code
- Conversion diff is [here](https://github.com/vinchamp77/Demo_SimpleNavigationCompose/compare/master..compose_destinations). 
- GitHub Repository: [Demo_SimpleNavigationCompose](https://github.com/vinchamp77/Demo_SimpleNavigationCompose/tree/compose_destinations) (compose_destinations branch)

---

## See Also
- [Simple Jetpack Compose Navigation Example](https://vtsen.hashnode.dev/simple-jetpack-compose-navigation-example)

---

*Originally published at [https://vtsen.hashnode.dev](https://vtsen.hashnode.dev/compose-destinations-navigation-library)*.