Android Navigation Component back button not working

Christilyn Arjona picture Christilyn Arjona · Jan 21, 2020 · Viewed 17.2k times · Source

I'm using the Navigation Component in android where I have set 6 fragments initially. The problem is when I added a new fragment (ProfileFragment).

When I navigate to this new fragment from the start destination, pressing the native back button does not pop the current fragment off. Instead, it just stays to the fragment I'm in - the back button does nothing.

Here's my navigation.xml:

<?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/dashboard_navigation"
    app:startDestination="@id/dashboardFragment"
    >

                <fragment
                    android:id="@+id/dashboardFragment"
                    android:name="com.devssocial.localodge.ui.dashboard.ui.DashboardFragment"
                    android:label="DashboardFragment"
                    >
                                <action
                                    android:id="@+id/action_dashboardFragment_to_newPostFragment"
                                    app:destination="@id/newPostFragment"
                                    app:enterAnim="@anim/slide_in_up"
                                    app:exitAnim="@anim/slide_out_down"
                                    app:popEnterAnim="@anim/slide_in_up"
                                    app:popExitAnim="@anim/slide_out_down"
                                    />
                                <action
                                    android:id="@+id/action_dashboardFragment_to_notificationsFragment"
                                    app:destination="@id/notificationsFragment"
                                    app:enterAnim="@anim/slide_in_up"
                                    app:exitAnim="@anim/slide_out_down"
                                    app:popEnterAnim="@anim/slide_in_up"
                                    app:popExitAnim="@anim/slide_out_down"
                                    />
                                <action
                                    android:id="@+id/action_dashboardFragment_to_mediaViewer"
                                    app:destination="@id/mediaViewer"
                                    app:enterAnim="@anim/slide_in_up"
                                    app:exitAnim="@anim/slide_out_down"
                                    app:popEnterAnim="@anim/slide_in_up"
                                    app:popExitAnim="@anim/slide_out_down"
                                    />
                                <action
                                    android:id="@+id/action_dashboardFragment_to_postDetailFragment"
                                    app:destination="@id/postDetailFragment"
                                    app:enterAnim="@anim/slide_in_up"
                                    app:exitAnim="@anim/slide_out_down"
                                    app:popEnterAnim="@anim/slide_in_up"
                                    app:popExitAnim="@anim/slide_out_down"
                                    />

                            ====================== HERE'S THE PROFILE ACTION ====================                                
                                <action
                                    android:id="@+id/action_dashboardFragment_to_profileFragment"
                                    app:destination="@id/profileFragment"
                                    app:enterAnim="@anim/slide_in_up"
                                    app:exitAnim="@anim/slide_out_down"
                                    app:popEnterAnim="@anim/slide_in_up"
                                    app:popExitAnim="@anim/slide_out_down"
                                    />
                            =====================================================================                                

                </fragment>



                <fragment
                    android:id="@+id/profileFragment"
                    android:name="com.devssocial.localodge.ui.profile.ui.ProfileFragment"
                    android:label="fragment_profile"
                    tools:layout="@layout/fragment_profile"
                    />
</navigation>

enter image description here

In the image above, the highlighted arrow (in the left) is the navigation action I'm having troubles with.

In my Fragment code, I'm navigating as follows:

findNavController().navigate(R.id.action_dashboardFragment_to_profileFragment)

The other navigation actions are working as intended. But for some reason, this newly added fragment does not behave as intended.

There are no logs showing when I navigate to ProfileFragment and when I press the back button.

Am I missing something? or is there anything wrong with my action/fragment configurations?

EDIT: I do not do anything in ProfileFragment. Here's the code for it:

class ProfileFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_profile, container, false)
    }


}

And my activity xml containing the nav host:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
        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">

    <fragment
            android:id="@+id/dashboard_navigation"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:navGraph="@navigation/dashboard_navigation"
            app:defaultNavHost="true"/>

</FrameLayout>

Answer

trOnk12 picture trOnk12 · Jun 3, 2020

For anyone using LiveData in a previous Fragment which is a Home Fragment, whenever you go back to the previous Fragment by pressing back button the Fragment is starting to observe the data and because ViewModel survives this operation it immediately emits the last emitted value which in my case opens the Fragment from which I pressed the back button, that way it looks like the back button is not working the solution for this is using something that emits data only once. I used this :

class SingleLiveData<T> : MutableLiveData<T>() {

private val pending = AtomicBoolean()

/**
 * Adds the given observer to the observers list within the lifespan of the given
 * owner. The events are dispatched on the main thread. If LiveData already has data
 * set, it will be delivered to the observer.
 *
 * @param owner The LifecycleOwner which controls the observer
 * @param observer The observer that will receive the events
 * @see MutableLiveData.observe
 */
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
    super.observe(owner, Observer { t ->
        if (pending.compareAndSet(true, false)) {
            observer.onChanged(t)
        }
    })
}

/**
 * Sets the value. If there are active observers, the value will be dispatched to them.
 *
 * @param value The new value
 * @see MutableLiveData.setValue
 */
@MainThread
override fun setValue(value: T?) {
    pending.set(true)
    super.setValue(value)
}