3

My app has a modal navigation drawer to navigate between home page and target page.

I tried to launch the app and navigate to the target page by tapping a notification containing a deeplink intent, and it actually worked. But when I pressed the home page button in the drawer, navigation didn't show me the home screen but stayed at the target screen.

Then I pressed the system back button, the screen turned to home page, and navigation works as I expected again, it can navigate between two pages.

I deleted the restoreState = true in the drawer navigation button, then I found the navigation works normally, but I couldn't restore the screen state.

So I wonder what caused this issue, did I created an independent activity at the top of the original activity? Or the restoreState restored the entire screen?

Here is my code:

AndroidManifest.xml

 <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:launchMode="singleTop" android:theme="@style/Theme.MyApp" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="home_page" android:scheme="myapp" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="target_page" android:scheme="myapp" /> </intent-filter> </activity> 

Code to send a notification:

 //screenRoute is "home_page" or "target_page" val deeplinkUri = "myapp://$screenRoute".toUri() val deepLinkIntent = Intent( Intent.ACTION_VIEW, deeplinkUri, ).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP } // Create PendingIntent val pendingIntent: PendingIntent = TaskStackBuilder.create(context).run { addNextIntentWithParentStack(deepLinkIntent) getPendingIntent( hashcode, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) } val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val builder = NotificationCompat.Builder(context, NotificationUtils.ALARM_CHANNEL_ID) .setContentTitle("Notification Title") .setSmallIcon(R.drawable.app_icon) .setContentText(message) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) .setAutoCancel(true) notificationManager.notify(1, builder.build()) 

Code of navigation drawer: I want to set these two pages as top-level pages, so I used popUpTo() to clear the backstack.

 DrawerNavigationButton( //....// onClick = { navController.navigate("home_page") { popUpTo(navController.graph.findStartDestination().id) { saveState = true } restoreState = true launchSingleTop = true } } ) DrawerNavigationButton( //....// onClick = { navController.navigate("target_page") { popUpTo(navController.graph.findStartDestination().id) { saveState = true } restoreState = true launchSingleTop = true } } ) 

And code about nav host:

 val uri = "myapp:/" NavHost( navController = navController, startDestination = "home_page" ) { //Home Page composable( route = "home_page", deepLinks = listOf( navDeepLink { uriPattern = "$uri/home_page" } ), ) { backStackEntry -> HomeScreen( //........// ) } //Target Page composable( route = "target_page", deepLinks = listOf( navDeepLink { uriPattern = "$uri/target_page" } ), ) { backStackEntry -> TargetScreen( //........// ) } } 

Any help would be greatly appreciated!

1 Answer 1

5

As per this issue and duplicates such as this one:

make sure you are wrapping each composable that belongs to a separate back stack in a navigation element - that's what Navigation uses to know that these are separate back stacks when it deep links.

This is further explained in the original issue (which uses fragments and XML, but the same thing applies to Navigation Compose):

Navigation's support for multiple back stacks (via saveState and restoreState) require that those flags be passed to the calls to navigate(). Otherwise, you are building a single back stack.

However, in the case of deep links, it is NavController that is calling navigate() on your behalf, building up the correct back stack, making it impossible to call saveState and actually build up multiple back stacks. This is most apparent when using NavigationUI and deep linking into another graph (e.g., a different graph than your start destination).

With this change, handleDeepLink() will automatically call saveState when your deep link links to another graph, ensuring that your multiple back stacks are built correctly.

For example, if you had three graphs: A (your starting graph), B, and C and you deep link to B1, your final back stack would be A1 -> B1 (as the start destination of your graph is automatically added to any deep link) and tapping on tab A would correctly return you to A1 since we've now call saveState when navigating to B1.

This support extends to explicit deep links as well with the same heuristic of navigating to another graph in place.

So what you actually want to use is:

NavHost( navController = navController, startDestination = "home_page" ) { //Home Page navigation( route = "home_page" startDestination = "home" ) { composable( route = "home", deepLinks = listOf( navDeepLink { uriPattern = "$uri/home_page" } ), ) { backStackEntry -> HomeScreen( //........// ) } } //Target Page navigation( route = "target_page" startDestination = "target" ) { composable( route = "target", deepLinks = listOf( navDeepLink { uriPattern = "$uri/target_page" } ), ) { backStackEntry -> TargetScreen( //........// ) } } } 
Sign up to request clarification or add additional context in comments.

1 Comment

It really works for me! It is the first question I asked in StackOverflow, I'm happy this issue can be solved, please accept my sincerest thanks ;)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.