Deep Links in Jetpack Compose: A Comprehensive Implementation Guide

Deep linking is a powerful mechanism in Android that allows users to navigate directly to a specific location within an application from an external source, such as a web link, push notification, or another app. In Jetpack Compose, implementing deep links requires integrating navigation components and setting up the necessary intent filters. This comprehensive guide will walk you through implementing deep links in a Jetpack Compose application.

What are Deep Links?

Deep links are URIs (Uniform Resource Identifiers) that direct users to a specific location within an application. Unlike regular app links, deep links require the user to select the application to handle the intent, which offers flexibility but may involve an extra step for the user.

Why Implement Deep Links?

  • Enhanced User Experience: Provides a direct route to relevant content within your app.
  • Improved Engagement: Increases user engagement by guiding users from external sources directly to specific features.
  • Marketing and Promotion: Supports marketing campaigns by directing users to particular promotions or content.

Prerequisites

  • Android Studio installed.
  • Basic understanding of Jetpack Compose.
  • Familiarity with Android Navigation Component.

How to Implement Deep Links in Jetpack Compose

Follow these steps to implement deep links in your Jetpack Compose application:

Step 1: Add Navigation Dependencies

Ensure that you have the necessary navigation dependencies in your build.gradle file:

dependencies {
    implementation("androidx.core:core-ktx:1.10.1")
    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")
    implementation("androidx.activity:activity-compose:1.7.2")
    implementation(platform("androidx.compose:compose-bom:2023.03.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")

    // Navigation dependencies
    implementation("androidx.navigation:navigation-compose:2.6.0")
}

Step 2: Define Navigation Graph

Create a navigation graph using the NavController to define the app’s navigation structure. This graph will include destinations that can be reached via deep links.


import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.compose.rememberNavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import androidx.navigation.navDeepLink
import androidx.compose.material3.Text
import androidx.compose.ui.tooling.preview.Preview

@Composable
fun NavigationGraph() {
    val navController = rememberNavController()

    NavHost(navController = navController, startDestination = "home") {
        composable("home") {
            HomeScreen(navController = navController)
        }
        composable(
            route = "details/{itemId}",
            arguments = listOf(navArgument("itemId") { type = NavType.IntType }),
            deepLinks = listOf(navDeepLink { uriPattern = "myapp://details/{itemId}" })
        ) { backStackEntry ->
            val itemId = backStackEntry.arguments?.getInt("itemId")
            DetailsScreen(itemId = itemId)
        }
    }
}

@Preview(showBackground = true)
@Composable
fun NavigationGraphPreview() {
    NavigationGraph()
}

In this example:

  • The NavHost defines the navigation graph, starting at the “home” destination.
  • The composable function associates a route (“details/{itemId}”) with the DetailsScreen composable.
  • navArgument specifies that “itemId” is an argument of type IntType.
  • navDeepLink sets up the deep link with the URI pattern “myapp://details/{itemId}”.

Step 3: Create UI Composable Screens

Define the composable screens for each destination in the navigation graph.


import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController

@Composable
fun HomeScreen(navController: NavController) {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "Go to Details",
            style = TextStyle(fontSize = 20.sp, color = MaterialTheme.colorScheme.primary),
            modifier = Modifier.clickable {
                navController.navigate("details/123")
            }
        )
    }
}

@Composable
fun DetailsScreen(itemId: Int?) {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text(text = "Details Screen: Item ID = ${itemId ?: "N/A"}", style = TextStyle(fontSize = 20.sp))
    }
}

In these composables:

  • HomeScreen displays a clickable text that navigates to the “details” route with a hardcoded item ID.
  • DetailsScreen displays the item ID passed as a parameter in the route.

Step 4: Configure the AndroidManifest.xml

Add an intent-filter to your AndroidManifest.xml file for the Activity that hosts the Compose UI.


    
        
        
        
        
    

    <!-- Main activity intent filter -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

In the AndroidManifest.xml:

  • The <activity> tag includes the android:exported="true" attribute to allow the activity to be launched by other applications.
  • The <intent-filter> tag with the android.intent.action.VIEW action enables the activity to handle incoming deep links.
  • The <category> tags specify that the activity is accessible from the browser (android.intent.category.BROWSABLE) and should be a default handler (android.intent.category.DEFAULT).
  • The <data> tag defines the URI scheme (myapp) and host (details) that the activity can handle.

Step 5: Integrate with MainActivity

Integrate the navigation graph with your MainActivity:


import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.example.deeplinkdemo.NavigationGraph

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NavigationGraph()
        }
    }
}

Testing Deep Links

To test the deep link, use the following ADB command:

adb shell am start -W -a android.intent.action.VIEW -d "myapp://details/123" com.example.deeplinkdemo

Replace com.example.deeplinkdemo with your application’s package name and adjust the URI as needed.

Conclusion

Deep links enhance the user experience by enabling direct navigation to specific content within your Jetpack Compose applications. By setting up the navigation graph, defining composable screens, and configuring the AndroidManifest.xml file, you can effectively implement deep links to improve user engagement and streamline navigation. Following this guide will help you create a more intuitive and user-friendly Android application.