androidandroid-jetpack-composeandroid-architecture-navigationjetpack-compose-navigationnavigation-compose

How to integrate AlertDialog with Navigation component in Jetpack Compose?


I am using Jetpack Compose and the Android navigation component. When I am on a screen with an AlertDialog, I am unable to navigate back. I guess it's due to the AlertDialog catching the back button event. However I don't know how to connect the AlertDialog to the navigation component? Is there any official way or best practice to do this? Here's my code:

// sample with a screen and a "navigate to dialog" button.
// once the button is pressed, an AlertDialog is shown.
// Using the back button while the AlertDialog is open has no effect ):    

@Composable
fun MyNavHost(navController: NavHostController, modifier: Modifier = Modifier) {
    NavHost(
        navController = navController,
        startDestination = "start_route",
        modifier = modifier
    ) {
        composable("start_route") {
            Text("start screen")
        }
        
        // this is my screen with the dialog
        dialog("dialog_route") {
            AlertDialog(
                onDismissRequest = {  /*TODO*/ }, // guess I need to connect this to the navigation component? bug how?
                title = {
                    Text(text = "Dialog title")
                },
                text = {
                    Text(text = "I am a dialog")
                },
                buttons = {}
            )
        }
    }
}


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

        setContent {
            MyJetpackComposeTheme {
                val navController = rememberNavController()

                Scaffold() { innerPadding ->

                    Column {
                        Button(onClick = { navController.navigate("dialog_route") }) {
                            Text("navigate to dialog")
                        }
                        MyNavHost(navController, modifier = Modifier.padding(innerPadding))
                    }
                }
            }
        }
    }
}

Solution

  • As per the dialog documentation:

    This is suitable only when this dialog represents a separate screen in your app that needs its own lifecycle and saved state, independent of any other destination in your navigation graph. For use cases such as AlertDialog, you should use those APIs directly in the composable destination that wants to show that dialog.

    So you shouldn't be using a dialog destination at all: a dialog destination is specifically and only for providing the content lambda of a regular Dialog. What your code is actually doing is creating an empty Dialog (i.e., you don't emit any composables in the lambda you pass to dialog, then creating another AlertDialog stacked on top of that empty dialog. That's not what you want to be doing.

    Instead, you should be following the AlertDialog docs and directly creating the AlertDialog where you want it to be used, setting your own boolean for when it should be shown/hidden.