Search code examples
androidkotlinandroid-architecture-navigationandroid-navigationandroid-navigation-graph

Android Navigation-Component: Back Button not navigating back to viewpager2


I have a Button in my viewpager2 (Page: UserDataFragment) which navigates the user to another screen (UserDataChangeEmailFragment), where they can change their password etc. The problem I encounter is that the back button on this screen is not working (e.g when the user presses back and or the toolbar button, the user is not navigated back to UserDataFragment).

The only error I then get is: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length.

Graph Picture (trying to navigate from x to y):

enter image description here

Graph Structure

UserDataHolderFragment (Holder for the ViewPager2)
 |
 |--UserDataFragment (First Page of the ViewPager2, here is the **Button**)
 |  |
 |  |-- UserDataChangeEmailFragment (Back button not working) 
 |
 |
 |--UserDataAddressFragment (Second Page of the ViewPager2)

Graph Code (Deleted unnecessary stuff)

<navigation android:id="@+id/nav_user_data"
    app:startDestination="@id/userDataHolderFragment">

    <fragment
        android:id="@+id/userDataHolderFragment"
        android:name="com.rsb3000.app.framework.ui.view.fragment.user.loggedIn.userdata.UserDataHolderFragment"
        tools:layout="@layout/fragment_user_data_holder" >
        <action
            android:id="@+id/action_userDataHolderFragment_to_userDataChangeEmailFragment"
            app:destination="@id/userDataChangeEmailFragment" />
        <action
            android:id="@+id/action_userDataHolderFragment_to_userDataChangePasswordFragment"
            app:destination="@id/userDataChangePasswordFragment" />
    </fragment>

    <fragment
        android:id="@+id/userDataFragment"
        android:name="com.rsb3000.app.framework.ui.view.fragment.user.loggedIn.userdata.UserDataFragment"
        tools:layout="@layout/fragment_user_data">
    </fragment>

    <fragment
        android:id="@+id/userDataAddressFragment"
        android:name="com.rsb3000.app.framework.ui.view.fragment.user.loggedIn.userdata.UserDataAddressFragment"
        tools:layout="@layout/fragment_user_data_address" >
    </fragment>

    <fragment
        android:id="@+id/userDataChangeEmailFragment"
        android:name="com.rsb3000.app.framework.ui.view.fragment.user.loggedIn.userdata.UserDataChangeEmailFragment"
        tools:layout="@layout/fragment_user_data_change_email" />

</navigation>

UserDataHolderFragment Code (Navigation button here)

@AndroidEntryPoint
class UserDataHolderFragment : Fragment(R.layout.fragment_user_data_holder) {
    private val binding: FragmentUserDataHolderBinding by viewBinding(FragmentUserDataHolderBinding::bind)

    val userDataViewPagerAdapter: UserDataViewPagerAdapter get() = UserDataViewPagerAdapter(SnackbarUtils(), this)
    @Inject lateinit var tabLayoutHelper: TabLayoutHelper

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.headline.userStandardToolbar.setupWithNavController(findNavController(), AppBarConfiguration(findNavController().graph))
        bindObjects()
        bindTablayout()
    }

    private fun bindObjects() {
        with(binding) {
            lifecycleOwner = viewLifecycleOwner
            vpUserData.adapter = userDataViewPagerAdapter
        }
    }

    private fun bindTablayout() {
        tabLayoutHelper.init(binding.tlUserData, binding.vpUserData) { tab, position ->
            when(position) {
                0 -> tab.text = requireContext().getString(R.string.fragment_user_data_holder_user_data_tab)
                1 -> tab.text = requireContext().getString(R.string.fragment_user_data_holder_user_address_tab)
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        tabLayoutHelper.onDestroyView()
        binding.vpUserData.adapter = null
    }
}

UserDataFragment

private fun bindBtnClicks() {
   btnGoToChangeAddress.setOnClickListener {
       findNavController().navigate(
          UserDataHolderFragmentDirections.actionUserDataFragmentHolderToUserDataChangeEmailFragment()
      )
   }
}

UserDataChangeEmailFragment (BACK BUTTON NOT WORKING HERE)

class UserDataChangeEmailFragment : Fragment(R.layout.fragment_user_data_change_email) {
    private val binding: FragmentUserDataChangeEmailBinding by viewBinding(FragmentUserDataChangeEmailBinding::bind)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.headline.userStandardToolbar.setupWithNavController(findNavController(), AppBarConfiguration(findNavController().graph))
    }
}

Solution

  • Okay, I've solved my error. This had nothing to do with my nav_graph nor the code in the fragment. The problem was that I navigated to the other fragment within the livedata. Clicking the back button immediately triggered the observation of the livedata and navigated me again and again.

    Solution: Before navigating, reset the livedata. You can see more solutions in this stackoverflow post