Search code examples
androidandroid-jetpack-composeback-buttonandroid-jetpack-navigation

Android Navigation: How to fix back button behavior in Jetpack Compose NavHost?


It seems like the composable NavHost from androidx.navigation:navigation-compose:2.5.3 does not satisfy navigation principles for back-button navigation as outlined here: https://developer.android.com/guide/navigation/navigation-principles

In particular: When opening the app using a deep link, the back button traverses up the app back stack, but it should leave the app instead. Am I missing anything? What can I do to correct the behavior?

Back-button behavior when opening the app with a details deep-link:

  • Intended: leave app
  • Actual: Navigates up the simulated app back stack

Example code to demonstrate this: AndroidManifest.xml: Add deep link support

<application>
    ...
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with "http://www.example.com/gizmos” -->
        <data android:scheme="http"
            android:host="www.example.com"
            android:pathPrefix="/gizmos" />
        <!-- note that the leading "/" is required for pathPrefix-->
    </intent-filter>
</application

MainActivity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    MainNavigation()
                }
            }
        }
    }
}

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

    NavHost(navController = navController, startDestination = "start") {
        navigation(
            startDestination = "list",
            route = "start",
        ) {
            composable("list") { ListScreen(navigateToDetail = {
                navController.navigate(route = "detail/$it")
            }) }
            composable(
                route = "detail/{id}",
                arguments = listOf(
                    navArgument(name = "id") { type = NavType.LongType }
                ),
                deepLinks = listOf(
                    navDeepLink {
                        uriPattern = "http://www.example.com/gizmos/{id}"
                    }
                )
            ) { backStackEntry ->
                val id = backStackEntry.arguments?.getLong("id")

                DetailScreen(id)
            }
        }
        // TODO: Add more destinations
    }
}

@Composable
fun ListScreen(
    navigateToDetail : (Long) -> Unit,
) {
    Text("List")
    Button(onClick = {
        navigateToDetail(42)
    }) {
        Text("Navigate to 42")
    }
}

@Composable
fun DetailScreen(
    id : Long?,
) {
    Text("Detail: $id")
}

Solution

  • The issue was not with jetpack compose navigation, but with the way I navigated to the app.

    When opening the app through an intent via adb, like in ./adb shell am start -W -a android.intent.action.VIEW -d "http://www.example.com/gizmos/1234" com.example.navanimation, then the intent isn't called from another app task and therefore the back button shouldn't navigate to the app that was open previously.