Activity destinations

In your navigation graph, a destination can be an activity. While it is best practice to have a single activity in your app, apps often use separate activities for distinct components or screen within an app. Activity destinations can be useful in such cases.

Compose and Kotlin DSL

Adding an activity destination to your navigation graph is essentially the same in both Compose and when using the Kotlin DSL with fragments. This is because when passing your NavGraph to your NavHost composable, you use the same createGraph() lambda.

For more information, see Fragments and the Kotlin DSL.

XML

Creating an activity destination is similar to creating a fragment destination. However, the nature of an activity destination is quite different.

By default, the Navigation library attaches the NavController to an Activity layout, and the active navigation graph is scoped to the active Activity. If a user navigates to a different Activity, the current navigation graph is no longer in scope. This means that an Activity destination should be considered an endpoint within a navigation graph.

To add an activity destination, specify the destination Activity with its fully qualified class name:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/navigation_graph"
    app:startDestination="@id/simpleFragment">

    <activity
        android:id="@+id/sampleActivityDestination"
        android:name="com.example.android.navigation.activity.DestinationActivity"
        android:label="@string/sampleActivityTitle" />
</navigation>

This XML is equivalent to the following call to startActivity():

Kotlin

startActivity(Intent(context, DestinationActivity::class.java))

Java

startActivity(new Intent(context, DestinationActivity.class));

You might have cases where this approach is not appropriate. For example, you might not have a compile-time dependency on the activity class, or you might prefer the level of indirection of going through an implicit intent. The intent-filter in the manifest entry for the destination Activity dictates how you need to structure the Activity destination.

For example, consider the following manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.navigation.activity">
    <application>
        <activity android:name=".DestinationActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <data
                    android:host="example.com"
                    android:scheme="https" />
                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

The corresponding Activity destination needs to be configured with action and data attributes matching those in the manifest entry:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/navigation_graph"
    app:startDestination="@id/simpleFragment">
    <activity
        android:id="@+id/localDestinationActivity"
        android:label="@string/localActivityTitle"
        app:action="android.intent.action.VIEW"
        app:data="https://example.com"
        app:targetPackage="${applicationId}" />
</navigation>

Specifying targetPackage to the current applicationId limits the scope to the current application, which includes the main app.

The same mechanism can be used for cases where you want a specific app to be the destination. The following example defines a destination to be an app with an applicationId of com.example.android.another.app.

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/navigation_graph"
    app:startDestination="@id/simpleFragment">
    <activity
        android:id="@+id/localDestinationActivity"
        android:label="@string/localActivityTitle"
        app:action="android.intent.action.VIEW"
        app:data="https://example.com"
        app:targetPackage="com.example.android.another.app" />
</navigation>

Dynamic arguments

The previous examples used fixed URLs to navigate to destinations. You might also need to support dynamic URLs where additional info is sent as part of the URL. For example, you might send a user ID in a URL with a format similar to https://example.com?userId=<actual user ID>.

In this case, instead of the data attribute, use dataPattern. You can then supply arguments to be substituted for named placeholders within the dataPattern value:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/navigation_graph"
    app:startDestination="@id/simpleFragment">
    <activity
        android:id="@+id/localDestinationActivity"
        android:label="@string/localActivityTitle"
        app:action="android.intent.action.VIEW"
        app:dataPattern="https://example.com?userId={userId}"
        app:targetPackage="com.example.android.another.app">
        <argument
            android:name="userId"
            app:argType="string" />
    </activity>
</navigation>

In this example, you can specify a userId value using either Safe Args or with a Bundle:

Kotlin

navController.navigate(
    R.id.localDestinationActivity,
    bundleOf("userId" to "someUser")
)

Java

Bundle args = new Bundle();
args.putString("userId", "someUser");
navController.navigate(R.id.localDestinationActivity, args);

This example substitutes someUser for {userId} and creates a URI value of https://example.com?userId=someUser.