Using compose destinations
Tags: android, kotlin, programming, computerscience
Published: 2022-04-12 14:47:15

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

<!-- wp:paragraph -->
<p>We will cover briefly:</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Current navigation in compose</li><li>Using compose destinations</li><li>(Optional) Modify existing test cases</li></ol>
<!-- /wp:list -->

{% youtube WGcknKt0jkE %}

<!-- wp:heading {"level":3} -->
<h3>Current navigation in&nbsp;compose</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>We get <a href="https://developer.android.com/jetpack/compose/navigation" rel="noreferrer noopener" target="_blank">Compose Navigation</a> from the Jetpack Compose. It provides a <a href="https://developer.android.com/guide/navigation" rel="noreferrer noopener" target="_blank">Navigation component</a> that allows you to navigate between different composable.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Let’s see it in action using our code example.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*2Y5SI5C5ax2oHHP0ywpodQ.png" alt="Navigation Component"/><figcaption>Navigation Component</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>We have our <code>NavigationComponent</code> which is composable. You can assume this file contains all the routes in our app. As we can see, there are 3 routes here namely&nbsp;: <code>Auth</code> <code>Home</code> and <code>AddTodo</code></li><li>The starting or the first route is the <code>Auth</code> and based on some logic(<a href="https://proandroiddev.com/google-signin-compose-a9afa67b7519" rel="noreferrer noopener" target="_blank">Sign in Google logic in our case</a>) it redirects to the <code>Home</code> while passing an object <code>user</code></li><li>All our routes need a Navigation Controller. The <code>NavController</code> is the central API for the Navigation component. It keeps track of the back stack of the screens in your app and the state of each screen.</li><li>We create a <code>NavController</code> by using the <code>rememberNavController()</code> method and pass it in our Views</li></ul>
<!-- /wp:list -->

```kotlin
val navController = rememberNavController()
```

<!-- wp:heading {"level":4} -->
<h4>Creating NavHost</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>Each <code>NavController</code> must be associated with a <code>NavHost</code> composable. The <code>NavHost</code> links the <code>NavController</code> with a navigation graph that specifies the composable destinations that you should be able to navigate between.</li><li>In order to tie this everything, we place the above <code>NavigationComponent</code> inside our main activity</li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*R6rClCdKYOmR1c_FfDYXgw.png" alt="Main Activity"/><figcaption>Main Activity</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>For navigating a route, we use the <code>navigate()</code> method. <code>navigate()</code> takes a single <code>String</code> parameter that represents the destination’s route.</li></ul>
<!-- /wp:list -->

```kotlin
// Navigate to some screen
navController.navigate("screen_route")
// Pop everything up to the "home" destination off the back stack before
// navigating to the "friendslist" destination
navController.navigate("friendslist") {
    popUpTo("home")
}

// Pop everything up to and including the "home" destination off
// the back stack before navigating to the "friendslist" destination
navController.navigate("friendslist") {
    popUpTo("home") { inclusive = true }
}
```
<!-- wp:heading {"level":4} -->
<h4>Passing custom arguments</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>In order to do this, you add argument placeholders to your route, similar to how you <a href="https://developer.android.com/guide/navigation/navigation-deep-link#implicit" rel="noreferrer noopener" target="_blank">add arguments to a deep link</a></li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>navController.navigate(Destinations.Home.replace("{user}",userJson))</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>By default, all arguments are parsed as strings. Next, you should extract the <code>NavArguments</code> from the <code>NavBackStackEntry</code> that is available in the lambda of the <code>composable()</code> function.</p>
<!-- /wp:paragraph -->

```kotlin
composable(Destinations.Home) { backStackEntry ->
    val userJson = backStackEntry.arguments?.getString("user")
    // DO YOUR PROCESSING AND CONVERT TO USER OBJECT
    HomeView(navController, userModel = userObject!!)
}
```

<!-- wp:paragraph -->
<p>If you are still reading, you may have realized is it too much work? And I agree with you. There is some boilerplate code, especially in the case of passing the custom arguments. <em>Well, can this be improved? Yes!!!</em></p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Using compose destinations</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Forget about the previous section, and start afresh. Assume you need to implement navigation inside your app.</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Introducing <a href="https://github.com/raamcosta/compose-destinations" rel="noreferrer noopener" target="_blank">Compose Destinations</a>. As per the documentation</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p><strong>Compose Destination</strong>: A KSP library that processes annotations and generates code that uses Official Jetpack Compose Navigation under the hood. It hides the complex, non-type-safe and boilerplate code you would have to write otherwise. Most APIs are either the same as with the Jetpack Components or inspired by them.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/0*owdq1scPQ9KhUZR5.png" alt="Compose Destination"/><figcaption>Compose Destination</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>Setup</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>Install the dependencies inside <code>build.gradle</code> of your app</li></ul>
<!-- /wp:list -->

```gradle
ksp 'io.github.raamcosta.compose-destinations:ksp:1.4.2-beta'
implementation 'io.github.raamcosta.compose-destinations:core:1.4.2-beta'
```
<!-- wp:list -->
<ul><li>Add the following inside the plugin of <code>build.gradle</code></li></ul>
<!-- /wp:list -->

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

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: Compose Destinations takes advantage of annotation processing (using KSP) to improve the usability of Compose Navigation.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:list -->
<ul><li>Include a kotlin block that defines the <code>sourceSets</code> for the generated code inside your <code>build.gradle</code></li></ul>
<!-- /wp:list -->

```kotlin
kotlin {
    sourceSets {
        debug {
            kotlin.srcDir("build/generated/ksp/debug/kotlin")
        }
        release {
            kotlin.srcDir("build/generated/ksp/release/kotlin")
        }
    }
}
```

<!-- wp:heading {"level":4} -->
<h4>Implement Navigation</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>So you still have the routes&nbsp;: <code>Auth</code> <code>Home</code> and <code>AddTodo</code> Now let’s see how to implement navigation. You have your existing composables. Let’s annotate them</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*lDsWlflc4xJPGKc0_YXwZA.png" alt="Compose Destination Annotation"/><figcaption>Compose Destination Annotation</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>We use the <code>Destination</code> annotation which comes from the <a href="https://github.com/raamcosta/compose-destinations" rel="noreferrer noopener" target="_blank">Compose Destinations</a></li><li>We mark our AuthScreen with <code>start = True</code> which implies this destination is the start destination of the navigation graph</li><li>Next, we change the type of our <code>navController</code> to <code>DestinationsNavigator</code></li></ul>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>DestinationsNavigator is a wrapper interface to NavController.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:list -->
<ul><li>For the other screens, let’s say AddTodo, we simply annotate it</li></ul>
<!-- /wp:list -->

```kotlin
@Destination
@Composable
fun AddTodoView(navController: DestinationsNavigator) {}
```

<!-- wp:list -->
<ul><li>Let’s run the command below which generates all the Destinations</li></ul>
<!-- /wp:list -->

```bash
./gradlew clean build
```

<!-- wp:paragraph -->
<p>If the result is a success, you should see the generated code inside the <code>build/generated/ksp/debug/kotlin</code></p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*ctCumI3xUMtYzMePrreA9A.png" alt="Generated Destinations using Compose Destinations"/><figcaption>Generated Destinations using Compose Destinations</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>Using NavHost</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In case you realized, we no longer need the <code>NavigationComponent</code> class.</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>One final thing remaining is to add the <code>NavHost</code> inside our <code>MainActivity</code></li></ul>
<!-- /wp:list -->

```kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContent {
      AppTheme {
          DestinationsNavHost(navGraph = NavGraphs.root)
      }
  }
 }
}
```

<!-- wp:list -->
<ul><li>Our <code>DestinationsNavHost</code> includes the destinations of navGraph. It includes all the composables annotated with <code>Destination</code> inside NavGraphs generated file.</li><li><code>NavGraphs</code> is a generated file that describes your navigation graphs and their destinations. By default, all destinations belong to the <code>NavGraphs.root</code></li><li>Finally, for navigating to screens we still follow the same convention, but now we have the Destinations (like <code>AddTodoViewDestination</code> etc) mapped to each of the views.</li></ul>
<!-- /wp:list -->

```kotlin
// Navigate to add view screen
navController.navigate(AddTodoViewDestination)

// Pop everything up to and including the "auth" destination off
// the back stack before navigating to the "Home" destination
navController.navigate(HomeViewDestination) {
    popUpTo(AuthScreenDestination.routeId) { inclusive = true }
}
```
<!-- wp:heading {"level":4} -->
<h4>Passing custom arguments</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Here’s why this gets interesting. Let’s see a case, for example, we have an Auth screen and we need to pass the user object to our Home screen.</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Let’s create a <a href="https://developer.android.com/guide/navigation/navigation-kotlin-dsl#custom-types" rel="noreferrer noopener" target="_blank">Parcelable</a> class <code>GoogleUserModel</code> This class basically contains the username and the email of the person once they <a href="https://proandroiddev.com/google-signin-compose-a9afa67b7519" rel="noreferrer noopener" target="_blank">signed in from google</a></li></ul>
<!-- /wp:list -->

```kotlin
@Parcelize
data class GoogleUserModel(
    val name: String?,
    val email: String?
) : Parcelable
```

<!-- wp:list -->
<ul><li>We modify our <code>Home</code> composable as below</li></ul>
<!-- /wp:list -->

```kotlin
@Destination
@Composable
fun HomeView(
    navController: DestinationsNavigator,
    userModel: GoogleUserModel,
) {}
```
<!-- wp:paragraph -->
<p>We add the parcelable class<code>GoogleUserModel</code> to the parameters and next, we again need to run our build command which updates the generated destination for the <code>HomeView</code></p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Inside our <code>AuthView</code> once we get the required data from the API, we create the <code>GoogleUserModel</code> and pass it to the <code>HomeViewDestination</code></li></ul>
<!-- /wp:list -->

```kotlin
navController.navigate(
    HomeViewDestination(
        GoogleUserModel(
            email = user.email,
            name = user.name,
        )
    )
) {
    popUpTo(route = AuthScreenDestination.routeId) {
        inclusive = true
    }
}
```

<!-- wp:paragraph -->
<p>After we navigate to <code>HomeView</code> we pop the routes, including <code>AuthView</code> by specifying the <code>AuthScreenDestination</code> inside the popUpTo.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Modify existing test&nbsp;cases</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>We created some tests based on the <code>NavigationComponent</code> (when we were using it) but since we longer have it with us, we make use of the <code>DestinationsNavigator</code></p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Since <code>DestinationsNavigator</code> is an interface, we create our own <code>DestinationsNavigatorImpl</code> the class.</li><li>Our <strong>DestinationsNavigatorImpl</strong> simply extends from the <strong>DestinationsNavigator</strong> class and we override all the methods from the <code>DestinationsNavigator</code></li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*QDJ09H4BtMynWLQ9je4mwQ.png" alt="DestinationsNavigatorImpl"/><figcaption>DestinationsNavigatorImpl</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Next, we simply replace the <code><a href="https://developer.android.com/reference/kotlin/androidx/navigation/testing/TestNavHostController" rel="noreferrer noopener" target="_blank">TestNavHostController</a></code> with the <code>DestinationsNavigatorImpl</code> inside our <a href="https://levelup.gitconnected.com/testing-in-compose-d09b59337e4e" rel="noreferrer noopener" target="_blank">existing tests</a>,</li></ul>
<!-- /wp:list -->

```kotlin
// PREVIOUS
private lateinit var navController: TestNavHostController
// NOW
private var navController = DestinationsNavigatorImpl()
```

<!-- wp:paragraph -->
<p>We replace <code>NavigationComponent</code>with <code>DestinationsNavHost</code> inside our tests</p>
<!-- /wp:paragraph -->

```kotlin
composeTestRule.setContent {
    
     // PREVIOUS 
     NavigationComponent()
     
     // NOW
     DestinationsNavHost(navGraph = NavGraphs.root)
 }
```

{% youtube WGcknKt0jkE %}

<!-- wp:paragraph -->
<p><a rel="noreferrer noopener" href="https://github.com/AseemWangoo/hands_on_kotlin" target="_blank"><em>Source code.</em></a>

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>