Starting in version 1.2.0, Navigation 3 supports deep linking to
destinations in your app using the DeepLinkRequest and
DeepLinkMatcher classes.
To support deep links in your app, complete the following steps:
- Define intent filters in your
AndroidManifest.xmlto specify which URIs your app can handle. - Create
DeepLinkMatcherinstances to map incoming requests to your navigation keys. - Match incoming requests in your activity's
onCreateoronNewIntentmethod and update your back stack accordingly.
Create a DeepLinkRequest
A DeepLinkRequest represents an incoming deep link. It contains a
DeepLinkUri and optional RequestExtras with additional
information, such as the intent action or MIME type.
// Create a request with a String URI val request = DeepLinkRequest(uri = "https://www.example.com/home") // Create a request from a DeepLinkUri val deepLinkUri = DeepLinkUri("https://www.example.com/home") val requestFromUri = DeepLinkRequest(uri = deepLinkUri) // Create a request with a URI and action val requestWithAction = DeepLinkRequest( uri = "https://www.example.com/home", extras = DeepLinkRequest.actionExtra("android.intent.action.VIEW") ) // Create a request with URI, action and mimeType val requestWithMimeType = DeepLinkRequest( uri = "https://www.example.com/image", extras = requestExtras { put(DeepLinkRequest.ActionExtrasKey, "android.intent.action.VIEW") put(DeepLinkRequest.Companion.MimeTypeExtrasKey, "image/png") } )
Provide DeepLinkRequest extras
To store additional information related to the deep link, use the
RequestExtras class. To make defining and instantiating extras type-safe,
the library provides the RequestExtrasKey interface and
requestExtras DSL.
Additionally, the library provides two extras keys and associated helpers:
MimeTypeExtrasKey: Used to store a MIME typeString.ActionExtrasKey(Android-only): Used to store anIntent's actionString.
val extras: RequestExtras = requestExtras { put(DeepLinkRequest.Companion.MimeTypeExtrasKey, "application/json") put(DeepLinkRequest.ActionExtrasKey, Intent.ACTION_VIEW) } // Access typed values using the get operator val mimeType: String? = extras[DeepLinkRequest.Companion.MimeTypeExtrasKey] val action: String? = extras[DeepLinkRequest.ActionExtrasKey] // Create extras using helper functions and combine them val mimeTypeExtras: RequestExtras = DeepLinkRequest.mimeTypeExtra("application/json") val combinedExtras: RequestExtras = extras + DeepLinkRequest.actionExtra(Intent.ACTION_VIEW)
To define your own custom extras, implement the RequestExtrasKey
interface with a typed generic:
// Define a custom typed key: object CampaignIdExtrasKey : RequestExtrasKey<String> val customExtras: RequestExtras = requestExtras { put(CampaignIdExtrasKey, "123") } val campaignId: String? = customExtras[CampaignIdExtrasKey]
You can also use emptyRequestExtras() to construct an empty instance, or
combine extras using the + (plus) and - (minus) operators.
Create a DeepLinkRequest from an Intent
On Android, you can create a DeepLinkRequest directly from an Intent. When
constructed this way, the DeepLinkRequest is built as follows:
- The
uriis copied from the intent'sdatafield. - If not null, the MIME type and action extras are set from the corresponding intent fields.
- All of the
intent.extraswith non-null values are saved as aSavedStateinDeepLinkRequest.IntentExtrasKey. - Any additional extras provided using the
extrasparameter are added.
object CampaignIdExtrasKey : RequestExtrasKey<String> val intent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("https://www.example.com/item/42") type = "application/json" putExtra("user_id", "123") } val request = DeepLinkRequest( intent = intent, extras = requestExtras { put(CampaignIdExtrasKey, "spring_promo") } ) // The resulting DeepLinkRequest contains: val uri = request.uri // "https://www.example.com/item/42" val action = request.extras[DeepLinkRequest.ActionExtrasKey] // "android.intent.action.VIEW" val mimeType = request.extras[DeepLinkRequest.Companion.MimeTypeExtrasKey] // "application/json" val intentExtras: SavedState? = request.extras[DeepLinkRequest.IntentExtrasKey] val userId: String? = intentExtras?.read { getStringOrNull("user_id") } // "123" val campaignId: String? = request.extras[CampaignIdExtrasKey] // "spring_promo"
Create DeepLinkMatcher instances
A DeepLinkMatcher maps incoming DeepLinkRequest instances to
navigation keys that can be added to your app's back stack. Navigation 3
provides three built-in matchers: UriDeepLinkMatcher for
pattern-based URI matching, StaticKeyDeepLinkMatcher for basic links, and
BackStackMatcher for building synthetic back stacks. The library also
supports custom matchers for use cases not covered by the built-in
matchers.
For more information, see Create DeepLinkMatchers.
Add intent filters
To enable a deep link to start your activity, you must define the matching
<intent-filter> elements in your app's AndroidManifest.xml. For more
information, see Add intent filters for incoming links.
Match an incoming request
After you've created your DeepLinkMatcher instances, you can match
incoming requests in your activity.
To match an incoming request, complete the following steps:
- Instantiate your
DeepLinkMatcherinstances. - Collate all of your
DeepLinkMatcherinstances, either explicitly or by using multibindings. - Create a
DeepLinkRequestfrom the incomingIntent. - Match the request against all matchers and find the best match.
- Create the back stack from the match result.
// 1. Instantiate your DeepLinkMatcher instances. val homeMatcher = StaticKeyDeepLinkMatcher(HomeKey, listOf(DeepLinkMatcher.actionFilter(Intent.ACTION_VIEW))) val userProfileMatcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/users/{id}"), serializer<UserProfileKey>() ).withBackStack { matchResult -> listOf(HomeKey, matchResult.key) } val telMatcher = TelDeepLinkMatcher() // 2. Collate all of your DeepLinkMatcher instances. // Note: Collating matchers with different generic types requires wildcards, // erasing the specific generic types. val deepLinkMatchers: List<DeepLinkMatcher<*, *>> = listOf( homeMatcher, userProfileMatcher, telMatcher ) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... // 3. Create a DeepLinkRequest from the incoming Intent val request = DeepLinkRequest(intent = intent) // 4. Match the request against all matchers and find the best match. // Because DeepLinkMatcher.MatchResult implements Comparable, you can // use maxOrNull() to find the best match. val matchResult = deepLinkMatchers .mapNotNull { it.match(request) } // List<DeepLinkMatcher.MatchResult<*>> .maxOrNull() // DeepLinkMatcher.MatchResult<*>? // 5. Create the back stack from the match result (or fall back to a default). val backStack: List<NavKey> = when (matchResult) { // If no match is found, use the default back stack (e.g., HomeKey) null -> listOf(HomeKey) // If a BackStackMatchResult is found, use the back stack from the result is BackStackMatchResult<*, *> -> { // Because star-projected matchers erase the key type, cast the back stack to List<NavKey>. @Suppress("UNCHECKED_CAST") matchResult.backStack as List<NavKey> } // Otherwise, use the key from the match result to make a single-item back stack else -> listOf(matchResult.key as NavKey) } } }