Android Navigation 3 Recipes

repository·main·Indexed 23 days ago

https://github.com/android/nav3-recipes

A collection of code recipes demonstrating common navigation patterns and use cases using Jetpack Navigation 3. Examples include implementing basic navigation with NavDisplay, managing persistent back stacks via rememberNavBackStack or Parcelable, configuring global and destination-specific animations, using BottomSheetSceneStrategy for modal bottom sheets, implementing multi-stack bottom navigation with TopLevelBackStack, and creating conditional navigation logic with a custom AppBackStack.

Tokens
22.9K
Snippets
28
Records
118
Agent score
79%

What's inside android-nav3-recipes

  1. Overview of Navigation 3 Recipes

    main

    This repository provides implementation recipes for Jetpack Navigation 3, a library for Android app navigation. Each recipe demonstrates a specific, isolated concept or use case (e.g., Deep Links, Scenes, Animations, or ViewModel integration) to help developers implement common navigation patterns.

    Note on Versions:

    • Recipes on the main branch use the latest version of Nav3 (which may be alpha or snapshot).
    • For stable versions, refer to the releases page.
  2. Deep Link Recipes Overview

    main

    The deep link module provides recipes for customizing how Navigation3 handles incoming deep links. It is structured into two primary functional areas:

    1. usecases: Contains custom implementations for parsing deep links, such as using a custom DeepLinkMatcher to extract data from DeepLinkRequest extras.
    2. handlerequests: Demonstrates how to process various deep link request types, including static URIs, URIs with arguments, and deep links that require a synthetic back stack to ensure correct 'Up' navigation behavior.
  3. Supported deep link patterns in the Basic Recipe

    main

    The basic deep link recipe demonstrates three common ways to structure deep link URLs and map them to NavKey types:

    • Exact URL: Maps to HomeKey (no arguments).
    • Path Arguments: Maps to UsersKey (arguments embedded in the URL path).
    • Query Arguments: Maps to SearchKey (arguments provided as query parameters).

    Implementation details for specific patterns are found in the MainActivity.deepLinkMatchers definition.

  4. Handle dialog dismissal in a SceneDecorationStrategy

    main

    When implementing the onDismissAll logic within a SceneDecorationStrategy to handle clicking outside the dialog, you must identify which entries to remove from the back stack.

    Caution on Content Keys: By default, contentKey uses key.toString(). If you are using simple object keys, you can use a pattern like:

    backStack.removeAll { it.toString() in contentKeys }

    However, if you are using complex keys or custom contentKey implementations, you may need to adjust this logic to ensure the correct entries are identified and dismissed.

  5. Deep Link Handling Patterns

    main

    Navigation 3 supports various ways to handle deep links. Key patterns include:

    • Static URIs: Using UriDeepLinkMatcher to handle fixed URIs.
    • URI with Arguments: Using UriDeepLinkMatcher to parse arguments from a URI.
    • Synthetic Backstack: Using DeepLinkMatcher.withBackStack to create a synthetic backstack for cross-app deep linking and correct 'Up' navigation.
    • Custom Matchers: Implementing a custom DeepLinkMatcher to parse data from DeepLinkRequest extras.
  6. Implement adaptive layouts with ListDetailSceneStrategy

    main

    The ListDetailSceneStrategy (provided via rememberListDetailSceneStrategy) enables adaptive list-detail layouts that automatically adjust the number of visible panes based on available screen width. It can transition between showing one, two, or three panes (List, Detail, and Extra) depending on the device's form factor.

    Pane Roles

    To use this strategy, you must assign roles to your destinations using metadata:

    • ListDetailSceneStrategy.listPane(): Designates the primary list content. This pane remains visible on larger screens. You can provide a placeholder to be displayed in the detail area when no specific detail is selected.
    • ListDetailSceneStrategy.detailPane(): Designates the secondary detail content.
    • ListDetailSceneStrategy.extraPane(): Designates tertiary content for very wide screens.

    Navigation is managed by standard back stack operations (adding and removing destinations). The ListDetailSceneStrategy observes the back stack and automatically updates the layout to show the appropriate panes.

  7. Create NavigationState and Navigator for state management

    main

    Navigation 3 uses a Unidirectional Data Flow (UDF) pattern. You need to implement two primary components:

    1. NavigationState: A state holder that manages the current top-level route and the back stacks for all top-level routes. It uses rememberNavBackStack to persist state.
    2. Navigator: A class that handles navigation events (like navigate and goBack) by modifying the NavigationState.

    Implementation Pattern

    Follow the provided code recipes to create NavigationState.kt and Navigator.kt. Once implemented, instantiate them in your UI layer using rememberNavigationState and remember { Navigator(navigationState) }.

    // Example instantiation in a Composable
    val navigationState = rememberNavigationState(
        startRoute = <Insert your starting route>,
        topLevelRoutes = <Insert your set of top level routes>
    )
    
    val navigator = remember { Navigator(navigationState) }
  8. How the Modular Navigation Recipe (Metro) works

    main

    This recipe implements a decoupled architecture for multi-module applications using Navigation 3 and Metro for dependency injection. The architecture separates navigation definitions from implementations across three distinct layers:

    1. app module: The orchestrator. It initializes a common Navigator and collects EntryProviderInstallers from various feature modules. It uses these installers to construct the final entryProvider used by the NavDisplay.
    2. common module: The core infrastructure. It defines the Navigator (which manages the back stack) and the EntryProviderInstaller type (a function used by features to contribute navigation entries).
    3. Feature modules: Split into two sub-modules to ensure decoupling:
      • api module: Contains the public navigation routes. Other modules depend on this to navigate to the feature without knowing how it is implemented.
      • impl module: Contains the actual UI (Composables) and an EntryProviderInstaller that maps routes to those Composables. This installer is injected into the app module using Metro's @ContributesTo and @IntoSet annotations.
  9. Handle different deep link request types in handlerequests

    main

    The handlerequests package provides specific implementations for common deep linking scenarios:

    • Static URIs (staticuri): Uses UriDeepLinkMatcher to handle deep links that match a fixed, static URI.
    • URIs with Arguments (uriwitharguments): Uses UriDeepLinkMatcher to handle deep links that contain dynamic arguments within the URI.
    • Synthetic Back Stacks (syntheticbackstack): Uses DeepLinkMatcher.withBackStack to handle deep links coming from other apps. This ensures that the 'Up' navigation behavior is correctly implemented by constructing a synthetic back stack.
  10. How deep linking with static URIs works

    main

    The deep link flow follows this lifecycle:

    1. Source: An external component (like StaticUriDeepLinkActivity) constructs an Intent containing the target URI.
    2. Matching: The target Activity (MainActivity) receives the intent and uses a UriDeepLinkMatcher (configured with a key serializer and URI pattern) to attempt to resolve the URI into a navigation key.
    3. Navigation: Based on the MatchResult from the matcher, the app either navigates to the resolved key or a fallback destination.
  11. Key Concepts of Custom DeepLink Matching

    main

    When building custom deep link logic in Navigation 3, understand these two core abstractions:

    • Custom RequestExtrasKey: A mechanism to define a unique, type-safe key (e.g., implementing RequestExtrasKey<String>) used to attach metadata or serialized payloads to a DeepLinkRequest.extras map.
    • Custom DeepLinkMatcher: A component that implements matchRequest(request). It is responsible for inspecting the DeepLinkRequest, extracting specific extras via a RequestExtrasKey, and transforming that data into a usable navigation key (e.g., a NavKey).