Android navigation component back Button doesn´t work [duplicate] - android

I use navigation components to navigate from one fragment to another. However, when the user press the back button, I want to navigate back to first fragment. But it keep showing the second fragment. This is my nav_graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment2"
android:name="com.myapp.ui.fragments.Fragment2"
android:label="fragment_2" />
<fragment
android:id="#+id/fragment1"
android:name="com.myapp.ui.fragments.Fragment1"
android:label="fragment_1">
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/fragment_fade_enter"
app:exitAnim="#anim/fragment_fade_exit"
app:popUpTo="#id/fragment1" />
</fragment>
</navigation>
And this is how I trigger the navigation in the code of my Fragment1-Class:
viewModel.itemSelected.observe(viewLifecycleOwner) {
navigate(it)
}
....
fun navigate(id: Long){
val bundle = Bundle()
bundle.putLong("itemid", id)
getNavController().navigate(R.id.action_fragment1_to_fragment2, bundle)
}
Edit:
Corrected startDestination in XML.
Edit2:
Added more code.

You're using a LiveData for an event. LiveData always caches the set value, so when you return to your Fragment1, you observe the LiveData again and get the same value a second time, causing you to navigate() yet again.
See this blog post for more information and alternatives.

Related

Cannot navigate to another Activity with Navigation Component

I'm trying to navigate to another activity (login) using Navigation Component.
Currently Activity_1 has navGraph1 and Activity_2 has navGraph2.
I've put a destination in navGraph1 for Activity_2. When I navigate, I get a blank screen, despite Activity_2 having a nav graph with a starting destination for another fragment.
onCreate is not being called, or is at least not being hit by my breakpoint in Activity_2.
When I nest navGraph2 in navGraph1, I can navigate to the starting destination fragment in navGraph2. This starting destination in navGraph2 is in compose. Activity_1 seems to bypass Activity_2 and hosts the fragment directly. However, I'd like Activity_2 to host it since navigating to Activity_2 can display more like a pop up modal, hiding things like the bottom nav bar.
I'm not sure what's going on here.
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/startFragment1">
<activity
android:id="#+id/Activity2"
android:name="com.example.Activity2"
android:label="Activity2" />
</navigation>
// in Activity 1
navController.navigate(R.id.activity2)
Activity 2
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_graph_2"
app:startDestination="#id/startFragment2">
<fragment
android:id="#+id/startFragment2"
android:name="com.example.StartFragment2"
android:label="StartFragment2" />
</navigation>
class Activity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity2)
}
}
// Layout activity 2
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph_2" />
</androidx.constraintlayout.widget.ConstraintLayout>
Found the solution. Activity 2 was overriding the wrong onCreate(). It should be the onCreate() with only the savedInstanceState parameter.

Android Custom Navigation Drawer has weird backstack

I have an app similar to Android Studios Navigation Drawer Activity:
My activity uses Android Architecture Navigation Components & a navigation drawer to navigate between different fragments.
As the navigation drawer is pretty custom i can't use the usual navigation-view, but use a custom fragment hosting a LinearLayout.
Each item in that LinearLayout has an onClickListener which boils down to
navController.navigate(R.id.myCorrespondingFragment)
So far, so everything works fine.
The problem begins when navigating back:
Let's imagine i navigate from "Home"-Fragment A -> B -> C and then go back.
Android Studios example behaves correctly: C -> A -> close
My implementation doesn't: it just pops the backstack C -> B -> A -> close
How do i fix that?
Minified Main-Layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout android:id="#+id/drawerLayout" >
<LinearLayout android:orientation="vertical" >
<androidx.appcompat.widget.Toolbar android:id="#+id/toolbar" />
<fragment
android:id="#+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph"
/>
</LinearLayout>
<fragment
android:name="com.company.drawer.DrawerFragment"
android:layout_gravity="start"
/>
</androidx.drawerlayout.widget.DrawerLayout>
Navigation graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/navigation_graph"
app:startDestination="#id/doorFragment"
>
<fragment
android:id="#+id/doorFragment"
android:name="com.company.door.DoorFragment"
android:label="#string/shared_empty"
/>
<fragment
android:id="#+id/historyFragment"
android:name="com.company.history.HistoryFragment"
android:label="#string/history"
/>
<fragment
android:id="#+id/settingsFragment"
android:name="com.company.settings.SettingsFragment"
android:label="#string/settings"
/>
</navigation>
Everything navigation related from my Main Activity's onCreate:
setSupportActionBar(toolbar)
val navController = (supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment).navController
toolbar.setupWithNavController(navController, AppBarConfiguration(
listOf(R.id.doorFragment, R.id.historyFragment, R.id.settingsFragment)
, drawerLayout
))
You can try like this
<fragment
android:id="#+id/fragmentA"
android:name="com.package.FragmentA"
android:label="Fragment A">
<action
android:id="#+id/action_fragmentA_to_fragmentB"
app:destination="#id/fragmentB"
app:popUpTo="#id/fragmentB"
app:popUpToInclusive="true / false" />
</fragment>
You can use global actions, so that you don't have to add an action for every fragment to fragment navigation but only for every fragment destination. This would look like this in XML:
<action android:id="#+id/action_global_doorFragment"
app:destination="#id/doorFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
<action android:id="#+id/action_global_historyFragment"
app:destination="#id/historyFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
<action android:id="#+id/action_global_settingsFragment"
app:destination="#id/historyFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
Note that I always used your start destination for popUpTo and also save the backstack state. You don't necessarily need the latter (restoreState and popUpToSaveState) for your use case but maybe want it later down the road when you have multiple fragments for a single drawer entry. This will get you the same behaviour as the current standard drawer integration.
Here's an easy example on how you can use the global action to go to your settings fragment. (The docs also explain how you can use global actions with the SafeArgs Gradle Plugin which is probably the better method.)
navController.navigate(R.id.action_global_settingsFragment)
If you don't want an extra action for every destination and your drawer menu items have the same id as its destination, you can also do it programmatically like this:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
navController.navigate(item.itemId, null, navOptions {
restoreState = true
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
})
return true
}

Previous fragment not loaded when pressing Back button using BottomNavigationView

I have an activity_main.xml with an BottomNavigationView and a NavHostFragment.
There are 2 tabs, HomeFragment with a video and FavoritesFragment. Within FavoritesFragment there is a list of favorite video's (thumbnails) and when I click on an item it loads the HomeFragment view with that specific video.
within mobile_navigation.xml I have defined the HomeFragment twice. 1 is the Home Tab and 1 is the "Favorite" when clicked on the thumbnail in FavoritesFragment.
When I click the back button I expect to be back in the FavoriteFragment. But the App shuts down.
When the thumbnail is clicked, this code is executed:
val action = FavoritesFragmentDirections
.actionNavigationFavoritesToNavigationFavorite(imageUri)
NavHostFragment.findNavController(this#FavoritesFragment)
.navigate(action)
I also tried this:
protected fun showFragment(fragment: Fragment, uniqueName: String) {
activity?.supportFragmentManager
?.beginTransaction()
?.replace(R.id.container, fragment)
?.addToBackStack(uniqueName)
?.commitAllowingStateLoss()
}
But same result.
With this:
override fun onBackPressed() {
super.onBackPressed()
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish()
}
}
The back button goes back to the HomeFragment, instead of FavoritesFragment.
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#+id/navigation_home">
<fragment
android:id="#+id/navigation_home"
android:name="eu.theappfactory.dailyrecipes.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home">
<argument
android:name="imageuri"
app:argType="string" />
</fragment>
<fragment
android:id="#+id/navigation_favorites"
android:name="eu.theappfactory.dailyrecipes.ui.favorites.FavoritesFragment"
android:label="#string/title_account"
tools:layout="#layout/fragment_favorites" >
<action
android:id="#+id/action_navigation_favorites_to_navigation_favorite"
app:destination="#id/navigation_favorite" />
</fragment>
<fragment
android:id="#+id/navigation_favorite"
android:name="eu.theappfactory.dailyrecipes.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home">
<argument
android:name="imageuri"
app:argType="string" />
</fragment>
</navigation>
Hey I don't know how kotlin works in android but here's how you should set it up,
onBack press only goes to the previous activity and if there isn't one it shuts down,
Since you have 1 activity and multiple fragments what you should do is override the onBackpress method and call the previous fragment and if none exist you should close the app, not after overriding onBackPress you will have to exit the app manually, I'm gonna share my java code, see if you can scavenge from it
private void onBackAction() {
getSupportFragmentManager().popBackStack();
}
Note the popBackStack only reopens a fragment if there is one the stack.
Try override onBackPressed method in the host activity and place the code:
override fun onBackPressed() {
if(!navController.popBackStack())
finish()
}

Navigate up by back button with Navigation Component

I use navigation components to navigate from one fragment to another. However, when the user press the back button, I want to navigate back to first fragment. But it keep showing the second fragment. This is my nav_graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment2"
android:name="com.myapp.ui.fragments.Fragment2"
android:label="fragment_2" />
<fragment
android:id="#+id/fragment1"
android:name="com.myapp.ui.fragments.Fragment1"
android:label="fragment_1">
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/fragment_fade_enter"
app:exitAnim="#anim/fragment_fade_exit"
app:popUpTo="#id/fragment1" />
</fragment>
</navigation>
And this is how I trigger the navigation in the code of my Fragment1-Class:
viewModel.itemSelected.observe(viewLifecycleOwner) {
navigate(it)
}
....
fun navigate(id: Long){
val bundle = Bundle()
bundle.putLong("itemid", id)
getNavController().navigate(R.id.action_fragment1_to_fragment2, bundle)
}
Edit:
Corrected startDestination in XML.
Edit2:
Added more code.
You're using a LiveData for an event. LiveData always caches the set value, so when you return to your Fragment1, you observe the LiveData again and get the same value a second time, causing you to navigate() yet again.
See this blog post for more information and alternatives.

Android Navigation library crash after sending data back

I use Android Navigation library for fragment navigation and try pass data back from second fragment to first. Do it with the next code:
findNavController().setGraph(R.navigation.nav_graph, bundle)
findNavController().popBackStack()
It works fine, but when I try to open the same screen after it, the next error occurred:
java.lang.IllegalArgumentException: navigation destination com.leshchenko.test:id/openPurchaseDetailsAction is unknown to this NavController
Thanks.
UPDATE:
My nav_graph.xml
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/purchasesListFragment">
<fragment
android:id="#+id/purchasesListFragment"
android:name="com.leshchenko.finance.presentation.purchases.PurchasesListFragment"
android:label="fragment_purchases_list"
tools:layout="#layout/fragment_purchases_list">
<action
android:id="#+id/openPurchaseDetailsAction"
app:destination="#+id/purchaseDetailsFragment">
<argument
android:name="purchaseId"
android:defaultValue="-1L"
app:argType="long" />
</action>
</fragment>
<fragment
android:id="#+id/purchaseDetailsFragment"
android:name="com.leshchenko.finance.presentation.purchase_details.PurchaseDetailsFragment"
android:label="fragment_purchase_details"
tools:layout="#layout/fragment_purchase_details">
<argument
android:name="purchaseId"
android:defaultValue="-1L"
app:argType="long" />
</fragment>
Details opens via:
findNavController().navigate(PurchasesListFragmentDirections.openPurchaseDetailsAction())
use findNavController().navigateUp() instead of findNavController().popBackStack()
I think you can just pass your data through viewModel and for fragment finish
just use :
findNavController().popBackStack()
eg:
view.setOnClickListener {
// pass Data through ViewModel
// viewModel.sendData(data)
Navigation.findNavController(it).popBackStack()
}

Categories

Resources