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>
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>
You can go back more than once. But when you reach the Home screen, you can't go back any further. Gesture navigation: Swipe from the left or right edge of the screen. 2-button navigation: Tap Back .
Toolbar toolbar = findViewById(R. id. toolbar); setSupportActionBar(toolbar); getSupportActionBar(). setDisplayHomeAsUpEnabled(true);
if you are using setupActionBarWithNavController in Navigation Component such as:
setupActionBarWithNavController(findNavController(R.id.fragment))
then also override and config this methods in your main activity:
override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.fragment) return navController.navigateUp() || super.onSupportNavigateUp() }
My MainActivity.kt
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupActionBarWithNavController(findNavController(R.id.fragment)) } override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.fragment) return navController.navigateUp() || super.onSupportNavigateUp() } }
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) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With