I have a very simple use case that I am surprised isn't working.
I have a bottom view navigation controller with 2 fragments, a list fragment and a detailed fragment. When I navigate to the detail fragment page I have no problems navigating back.
When I navigate to the detail fragment using explicitly using an item click handler in a recyclerview I can no longer navigate back to the first tab, the list tab using the bottom navigation.
Here 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"
android:id="@+id/job_nav_graph"
app:startDestination="@id/jobsListFragment">
<fragment
android:id="@+id/jobsListFragment"
android:name="com.plcoding.posterpalfeature.ui.fragments.JobsListFragment"
android:label="JobsListFragment" >
<action
android:name="listToDetail"
android:id="@+id/action_jobsListFragment_to_jobDetailFragment"
app:destination="@id/jobDetailFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right">
</action>
</fragment>
<fragment
android:id="@+id/jobDetailFragment"
android:name="com.plcoding.posterpalfeature.ui.fragments.JobDetailFragment"
android:label="JobDetailFragment" >
<action
android:id="@+id/action_jobDetailFragment_to_jobsListFragment"
app:destination="@id/jobsListFragment"
app:enterAnim="@anim/slide_in_left"
app:exitAnim="@anim/slide_out_right"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
</navigation>
main activity
<layout>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.plcoding.posterpalfeature.ui.activity.MainActivity">
<FrameLayout
android:id="@+id/flFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<fragment
android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/job_nav_graph" />
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="56dp"
app:menu="@menu/bottom_navigation_menu"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
JobDetails
<layout>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_job_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Fragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Joblist
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_jobs_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
item callback that doesn't allow me to use bottomnavigation to get back after using to navigate back to joblist after using to navigate to job details
private fun setupItemClickListener() {
jobsAdapter.setOnItemClickListener {
val action = JobsListFragmentDirections.actionJobsListFragmentToJobDetailFragment().setJobId(it.job.job_id.toInt())
findNavController().navigate(action)
}
}
JobListFragment
androidx.navigation.fragment.findNavController
//import com.plcoding.multipleroomtables.databinding.FragmentJobListBinding
import com.plcoding.posterpalfeature.api.Resource
import com.plcoding.posterpalfeature.repo.JobsRepository
import com.plcoding.posterpalfeature.ui.activity.MainActivity
import com.plcoding.posterpalfeature.ui.MainViewModel
import com.plcoding.posterpalfeature.ui.adapters.JobAdapter
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
class JobsListFragment : Fragment(R.layout.fragment_job_list){
companion object {
const val BUNDLE_KEY = "job_id"
const val TAG = "JobsListFragment"
}
@Inject
lateinit var repo : JobsRepository
lateinit var viewModel: MainViewModel
lateinit var binding: FragmentJobListBinding
lateinit var jobsAdapter : JobAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentJobListBinding.bind(requireView())
viewModel = (activity as MainActivity).mainViewModel
jobsAdapter = JobAdapter()
setupRecyclerView(binding.rvJobsList)
setupItemClickListener()
listenToApiLiveData()
viewModel.getJobsList()
}
private fun setupItemClickListener() {
jobsAdapter.setOnItemClickListener {
val action = JobsListFragmentDirections.actionJobsListFragmentToJobDetailFragment().setJobId(it.job.job_id.toInt())
findNavController().navigate(action)
}
}
private fun listenToApiLiveData() {
viewModel.jobsListUpdate.observe(viewLifecycleOwner, Observer { response ->
when(response) {
is Resource.Success -> {
hideProgressBar()
hideErrorMessage()
response.data?.let { jobsResponse ->
jobsAdapter.differ.submitList(jobsResponse.jobs)
jobsAdapter.notifyDataSetChanged()
}
}
is Resource.Error -> {
hideProgressBar()
response.message?.let { message ->
Toast.makeText(activity, "An error occured fetching jobs: $message", Toast.LENGTH_LONG).show()
}
}
is Resource.Loading -> {
showProgressBar()
}
}
} )
}
private fun showProgressBar() {
// paginationProgressBar.visibility = View.VISIBLE
// isLoading = true
}
private fun hideErrorMessage() {
}
private fun hideProgressBar() {
}
private fun setupRecyclerView(recyclerView: RecyclerView) {
activity?.lifecycleScope?.launch {
val jobslist = repo.getAllJobs()
recyclerView.apply {
adapter = jobsAdapter
layoutManager = LinearLayoutManager(activity)
//x addItemDecoration(DividerItemDecoration(this.context, DividerItemDecoration.VERTICAL))
}
jobsAdapter.differ.submitList(jobslist)
}
}
}
JobDetailFragment
package com.plcoding.posterpalfeature.ui.fragments
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import com.plcoding.multipleroomtables.R
import com.plcoding.multipleroomtables.databinding.FragmentJobDetailBinding
//import com.plcoding.multipleroomtables.databinding.FragmentJobDetailBinding
import com.plcoding.posterpalfeature.repo.relations.jobworkerposter.JobPW
import com.plcoding.posterpalfeature.ui.activity.MainActivity
import com.plcoding.posterpalfeature.ui.MainViewModel
class JobDetailFragment: Fragment(R.layout.fragment_job_detail) {
companion object {
const val TAG = "JobDetailFragment"
}
val args: JobDetailFragmentArgs by navArgs()
lateinit var binding: FragmentJobDetailBinding
lateinit var viewModel: MainViewModel
lateinit var jobPW: JobPW
var job_id: Int = -1
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentJobDetailBinding.bind(requireView())
job_id = args.jobId
viewModel = (activity as MainActivity).mainViewModel
jobPW = viewModel.getJobById(job_id.toLong())
jobPW.job
jobPW.posters
jobPW.workers
Log.d(TAG, "job_Id by sage args args.jobId ${job_id}, posters size ${jobPW.posters.size} " +
" workers size ${jobPW.workers.size} ")
}
}
This is very simple and should be working.
Any ideas of what to try next?
So the issue was with the backstack. I had to add popUp & popUpInclusive to the XML navigation fragment
This really was not clear from documentation and codelabs this should be default behaviour with BottomNavigationView. If someone can comment on how the popUp attributes work would be very much appreciated. Thank you for your support in the comments :)