Android Jetpack Navigation, BottomNavigationView with Youtube or Instagram like proper back navigation...












36















Android Jetpack Navigation, BottomNavigationView with auto fragment back stack on back button click?



What I wanted, after choosing multiple tabs one after another by user and user click on back button app must redirect to the last page he/she opened.



I achieved the same using Android ViewPager, by saving the currently selected item in an ArrayList. Is there any auto back stack after Android Jetpack Navigation Release? I want to achieve it using navigation graph



activity_main.xml



<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>


navigation.xml



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home"
android:title="@string/title_home" />

<item
android:id="@+id/navigation_people"
android:icon="@drawable/ic_group"
android:title="@string/title_people" />

<item
android:id="@+id/navigation_organization"
android:icon="@drawable/ic_organization"
android:title="@string/title_organization" />

<item
android:id="@+id/navigation_business"
android:icon="@drawable/ic_business"
android:title="@string/title_business" />

<item
android:id="@+id/navigation_tasks"
android:icon="@drawable/ic_dashboard"
android:title="@string/title_tasks" />

</menu>


also added



bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


I got one answer from Levi Moreira, as follows



navigation.setOnNavigationItemSelectedListener {item ->

onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

}


But by doing this only happening is that last opened fragment's instance creating again.



Providing proper Back Navigation for BottomNavigationView










share|improve this question

























  • Hi @BincyBaby i need same thing did you get any solutions?

    – Ramki Anba
    Aug 10 '18 at 4:28











  • not yet got answer

    – Bincy Baby
    Aug 12 '18 at 11:28






  • 2





    Commenting a bit late but upon some digging I found that the popBackStack is called from the NavController.navigate() function when NavOptions are not null. My guess is that at the moment it is not possible to do it out of the box. A custom implementation of NavController is required that accesses the mBackStack through reflection or something like that.

    – HawkPriest
    Sep 6 '18 at 14:18






  • 1





    If you add a listener to the bottom nav you can override the navigation so that it will pop back stack if the stack already contains the new destination or otherwise perform the normal navigation if it doesn't. if (!navHost.popBackStack(it.itemId, false)) navHost.navigate(it.itemId)

    – Marcus Hooper
    Oct 18 '18 at 6:25













  • A workaround for the fragment recreation problem - stackoverflow.com/a/51684125/6024687

    – jL4
    Dec 6 '18 at 5:55
















36















Android Jetpack Navigation, BottomNavigationView with auto fragment back stack on back button click?



What I wanted, after choosing multiple tabs one after another by user and user click on back button app must redirect to the last page he/she opened.



I achieved the same using Android ViewPager, by saving the currently selected item in an ArrayList. Is there any auto back stack after Android Jetpack Navigation Release? I want to achieve it using navigation graph



activity_main.xml



<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>


navigation.xml



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home"
android:title="@string/title_home" />

<item
android:id="@+id/navigation_people"
android:icon="@drawable/ic_group"
android:title="@string/title_people" />

<item
android:id="@+id/navigation_organization"
android:icon="@drawable/ic_organization"
android:title="@string/title_organization" />

<item
android:id="@+id/navigation_business"
android:icon="@drawable/ic_business"
android:title="@string/title_business" />

<item
android:id="@+id/navigation_tasks"
android:icon="@drawable/ic_dashboard"
android:title="@string/title_tasks" />

</menu>


also added



bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


I got one answer from Levi Moreira, as follows



navigation.setOnNavigationItemSelectedListener {item ->

onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

}


But by doing this only happening is that last opened fragment's instance creating again.



Providing proper Back Navigation for BottomNavigationView










share|improve this question

























  • Hi @BincyBaby i need same thing did you get any solutions?

    – Ramki Anba
    Aug 10 '18 at 4:28











  • not yet got answer

    – Bincy Baby
    Aug 12 '18 at 11:28






  • 2





    Commenting a bit late but upon some digging I found that the popBackStack is called from the NavController.navigate() function when NavOptions are not null. My guess is that at the moment it is not possible to do it out of the box. A custom implementation of NavController is required that accesses the mBackStack through reflection or something like that.

    – HawkPriest
    Sep 6 '18 at 14:18






  • 1





    If you add a listener to the bottom nav you can override the navigation so that it will pop back stack if the stack already contains the new destination or otherwise perform the normal navigation if it doesn't. if (!navHost.popBackStack(it.itemId, false)) navHost.navigate(it.itemId)

    – Marcus Hooper
    Oct 18 '18 at 6:25













  • A workaround for the fragment recreation problem - stackoverflow.com/a/51684125/6024687

    – jL4
    Dec 6 '18 at 5:55














36












36








36


18






Android Jetpack Navigation, BottomNavigationView with auto fragment back stack on back button click?



What I wanted, after choosing multiple tabs one after another by user and user click on back button app must redirect to the last page he/she opened.



I achieved the same using Android ViewPager, by saving the currently selected item in an ArrayList. Is there any auto back stack after Android Jetpack Navigation Release? I want to achieve it using navigation graph



activity_main.xml



<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>


navigation.xml



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home"
android:title="@string/title_home" />

<item
android:id="@+id/navigation_people"
android:icon="@drawable/ic_group"
android:title="@string/title_people" />

<item
android:id="@+id/navigation_organization"
android:icon="@drawable/ic_organization"
android:title="@string/title_organization" />

<item
android:id="@+id/navigation_business"
android:icon="@drawable/ic_business"
android:title="@string/title_business" />

<item
android:id="@+id/navigation_tasks"
android:icon="@drawable/ic_dashboard"
android:title="@string/title_tasks" />

</menu>


also added



bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


I got one answer from Levi Moreira, as follows



navigation.setOnNavigationItemSelectedListener {item ->

onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

}


But by doing this only happening is that last opened fragment's instance creating again.



Providing proper Back Navigation for BottomNavigationView










share|improve this question
















Android Jetpack Navigation, BottomNavigationView with auto fragment back stack on back button click?



What I wanted, after choosing multiple tabs one after another by user and user click on back button app must redirect to the last page he/she opened.



I achieved the same using Android ViewPager, by saving the currently selected item in an ArrayList. Is there any auto back stack after Android Jetpack Navigation Release? I want to achieve it using navigation graph



activity_main.xml



<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>


navigation.xml



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home"
android:title="@string/title_home" />

<item
android:id="@+id/navigation_people"
android:icon="@drawable/ic_group"
android:title="@string/title_people" />

<item
android:id="@+id/navigation_organization"
android:icon="@drawable/ic_organization"
android:title="@string/title_organization" />

<item
android:id="@+id/navigation_business"
android:icon="@drawable/ic_business"
android:title="@string/title_business" />

<item
android:id="@+id/navigation_tasks"
android:icon="@drawable/ic_dashboard"
android:title="@string/title_tasks" />

</menu>


also added



bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


I got one answer from Levi Moreira, as follows



navigation.setOnNavigationItemSelectedListener {item ->

onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

}


But by doing this only happening is that last opened fragment's instance creating again.



Providing proper Back Navigation for BottomNavigationView







android bottomnavigationview android-architecture-components android-navigation android-jetpack






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Aug 12 '18 at 10:33







Bincy Baby

















asked May 29 '18 at 6:06









Bincy BabyBincy Baby

89612245




89612245













  • Hi @BincyBaby i need same thing did you get any solutions?

    – Ramki Anba
    Aug 10 '18 at 4:28











  • not yet got answer

    – Bincy Baby
    Aug 12 '18 at 11:28






  • 2





    Commenting a bit late but upon some digging I found that the popBackStack is called from the NavController.navigate() function when NavOptions are not null. My guess is that at the moment it is not possible to do it out of the box. A custom implementation of NavController is required that accesses the mBackStack through reflection or something like that.

    – HawkPriest
    Sep 6 '18 at 14:18






  • 1





    If you add a listener to the bottom nav you can override the navigation so that it will pop back stack if the stack already contains the new destination or otherwise perform the normal navigation if it doesn't. if (!navHost.popBackStack(it.itemId, false)) navHost.navigate(it.itemId)

    – Marcus Hooper
    Oct 18 '18 at 6:25













  • A workaround for the fragment recreation problem - stackoverflow.com/a/51684125/6024687

    – jL4
    Dec 6 '18 at 5:55



















  • Hi @BincyBaby i need same thing did you get any solutions?

    – Ramki Anba
    Aug 10 '18 at 4:28











  • not yet got answer

    – Bincy Baby
    Aug 12 '18 at 11:28






  • 2





    Commenting a bit late but upon some digging I found that the popBackStack is called from the NavController.navigate() function when NavOptions are not null. My guess is that at the moment it is not possible to do it out of the box. A custom implementation of NavController is required that accesses the mBackStack through reflection or something like that.

    – HawkPriest
    Sep 6 '18 at 14:18






  • 1





    If you add a listener to the bottom nav you can override the navigation so that it will pop back stack if the stack already contains the new destination or otherwise perform the normal navigation if it doesn't. if (!navHost.popBackStack(it.itemId, false)) navHost.navigate(it.itemId)

    – Marcus Hooper
    Oct 18 '18 at 6:25













  • A workaround for the fragment recreation problem - stackoverflow.com/a/51684125/6024687

    – jL4
    Dec 6 '18 at 5:55

















Hi @BincyBaby i need same thing did you get any solutions?

– Ramki Anba
Aug 10 '18 at 4:28





Hi @BincyBaby i need same thing did you get any solutions?

– Ramki Anba
Aug 10 '18 at 4:28













not yet got answer

– Bincy Baby
Aug 12 '18 at 11:28





not yet got answer

– Bincy Baby
Aug 12 '18 at 11:28




2




2





Commenting a bit late but upon some digging I found that the popBackStack is called from the NavController.navigate() function when NavOptions are not null. My guess is that at the moment it is not possible to do it out of the box. A custom implementation of NavController is required that accesses the mBackStack through reflection or something like that.

– HawkPriest
Sep 6 '18 at 14:18





Commenting a bit late but upon some digging I found that the popBackStack is called from the NavController.navigate() function when NavOptions are not null. My guess is that at the moment it is not possible to do it out of the box. A custom implementation of NavController is required that accesses the mBackStack through reflection or something like that.

– HawkPriest
Sep 6 '18 at 14:18




1




1





If you add a listener to the bottom nav you can override the navigation so that it will pop back stack if the stack already contains the new destination or otherwise perform the normal navigation if it doesn't. if (!navHost.popBackStack(it.itemId, false)) navHost.navigate(it.itemId)

– Marcus Hooper
Oct 18 '18 at 6:25







If you add a listener to the bottom nav you can override the navigation so that it will pop back stack if the stack already contains the new destination or otherwise perform the normal navigation if it doesn't. if (!navHost.popBackStack(it.itemId, false)) navHost.navigate(it.itemId)

– Marcus Hooper
Oct 18 '18 at 6:25















A workaround for the fragment recreation problem - stackoverflow.com/a/51684125/6024687

– jL4
Dec 6 '18 at 5:55





A workaround for the fragment recreation problem - stackoverflow.com/a/51684125/6024687

– jL4
Dec 6 '18 at 5:55












6 Answers
6






active

oldest

votes


















26














You don't really need a ViewPager to work with BottomNavigation and the new Navigation architecture component. I have been working in a sample app that uses exactly the two, see here.



The basic concept is this, you have the main activity that will host the BottomNavigationView and that is the Navigation host for your navigation graph, this is how the xml for it look like:



activity_main.xml



<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>


The navigation Menu (tabs menu) for the BottomNavigationView looks like this:



navigation.xml



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home"
android:title="@string/title_home" />

<item
android:id="@+id/navigation_people"
android:icon="@drawable/ic_group"
android:title="@string/title_people" />

<item
android:id="@+id/navigation_organization"
android:icon="@drawable/ic_organization"
android:title="@string/title_organization" />

<item
android:id="@+id/navigation_business"
android:icon="@drawable/ic_business"
android:title="@string/title_business" />

<item
android:id="@+id/navigation_tasks"
android:icon="@drawable/ic_dashboard"
android:title="@string/title_tasks" />

</menu>


All of this is just the BottomNavigationView setup. Now to make it work with the Navigation Arch Component you need to go into the navigation graph editor, add all your fragment destinations (in my case I have 5 of them, one for each tab) and set the id of the destination with the same name as the one in the navigation.xml file:



enter image description here



This will tell android to make a link between the tab and the fragment, now every time the user clicks the "Home" tab android will take care of loading up the correct fragment.
There is also one piece of kotlin code that needs to be added to your NavHost (the main activity) to wire things up with the BottomNavigationView:



You need to add in your onCreate:



bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


This tells android to do the wiring between the Navigation architecture component and the BottomNavigationView. See more in the docs.



To get the same beahvior you have when you use youtube, just add this:



navigation.setOnNavigationItemSelectedListener {item ->

onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

}


This will make destinations go into the backstack so when you hit the back button, the last visited destination will be popped up.






share|improve this answer





















  • 3





    The secret sauce was adding the id in the nav graph. I'm using Navigation Drawer, but the principal is the same

    – JamesSugrue
    May 31 '18 at 23:21






  • 3





    but back navigation is not working properly

    – Bincy Baby
    Jun 1 '18 at 18:04











  • Can we have single instance of fragment ?

    – Bincy Baby
    Jun 2 '18 at 10:12






  • 10





    This is working fine with the back-button. But if user click on bottom tabs its not restoring the previously open child-fragment of that tab(if available). It just opening the new instant of (parent) fragment every time user click on bottom tabs. So this way will lead to a confusing/ frustrating nevigation experience to the users if navigated using bottom tabs many times. Dangerous implementation

    – Niroshan
    Jun 25 '18 at 5:26






  • 2





    There's no max number, but material design rules state you should only have 5 tabs max

    – Levi Moreira
    Sep 30 '18 at 20:36



















15














You have to set host navigation like below xml:



<LinearLayout 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"
android:orientation="vertical">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary" />

<fragment
android:id="@+id/navigation_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemIconTint="@drawable/color_state_list"
app:itemTextColor="@drawable/color_state_list"
app:menu="@menu/menu_bottom_navigation" />
</LinearLayout>


Setup With Navigation Controller :



NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_host_fragment);
NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());


menu_bottom_navigation.xml :



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@id/tab1" // Id of navigation graph
android:icon="@mipmap/ic_launcher"
android:title="@string/tab1" />
<item
android:id="@id/tab2" // Id of navigation graph
android:icon="@mipmap/ic_launcher"
android:title="@string/tab2" />

<item
android:id="@id/tab3" // Id of navigation graph
android:icon="@mipmap/ic_launcher"
android:title="@string/tab3" />
</menu>


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/tab1">
<fragment
android:id="@+id/tab1"
android:name="com.navigationsample.Tab1Fragment"
android:label="@string/tab1"
tools:layout="@layout/fragment_tab_1" />

<fragment
android:id="@+id/tab2"
android:name="com.navigationsample.Tab2Fragment"
android:label="@string/tab2"
tools:layout="@layout/fragment_tab_2"/>

<fragment
android:id="@+id/tab3"
android:name="com.simform.navigationsample.Tab3Fragment"
android:label="@string/tab3"
tools:layout="@layout/fragment_tab_3"/>
</navigation>


By setting up the same id of "nav_graph" to "menu_bottom_navigation" will handle the click of bottom navigation.



You can handle back action using popUpTo property in action tag.
enter image description here






share|improve this answer


























  • can you elaborate use of popUpTo ?

    – Bincy Baby
    Aug 12 '18 at 10:49











  • @BincyBaby popUpTo property helps you to return on particular fragment on back press.

    – SANAT
    Aug 18 '18 at 15:51






  • 1





    @SANAT but how to set up the popUpTo to the fragment imediatelly pressed before? Like, if you were in frag1 went to frag2 and then to frag3, back press should go back to frag2. If you were in frag1 and went directly to frag3, back press sould go back to frag1. The popUpTo seems to only let you choose one fragment to go back idependently of the user path.

    – Allan Veloso
    Jan 30 at 22:57



















8














You can have a viewpager setup with bottom navigation view. Each fragment in the viewpager will be a container fragment, it will have child fragments with its own backstack. You can maintain backstack for each tab in viewpager this way






share|improve this answer
























  • I was using that way, but app starting takes too much time to first launch

    – Bincy Baby
    May 29 '18 at 6:16











  • Then you must be doing something wrong, make sure you are not doing some heavy work in the oncreate or oncreateview of the fragments. There is no way it would take time

    – Suhaib Roomy
    May 29 '18 at 6:21











  • I have to load contents , i don't think youtube or instagram used ViewPager

    – Bincy Baby
    May 29 '18 at 6:48











  • Its definitely a viewpager. Just scroll on one page and try switiching tabs, its really fast and it resumes from the same state. There is no way you can achieve it by changing fragments on the same container, these are multiple fragments viewed using a viewpager

    – Suhaib Roomy
    May 29 '18 at 9:28











  • My guess is also that YouTube or Instagram do not use ViewPager. The restoring happens because of the backStack pop action that resumes the underlying fragment that is added in the first place not replaced

    – HawkPriest
    Sep 6 '18 at 14:13



















4














I have made an app like this (still not published on PlayStore) that has the same navigation, maybe its implementation is different from what Google does in their apps, but the functionality is the same.



the structure involves I have Main Activity that I switch the content of it by showing/hiding fragments using:



public void switchTo(final Fragment fragment, final String tag /*Each fragment should have a different Tag*/) {

// We compare if the current stack is the current fragment we try to show
if (fragment == getSupportFragmentManager().getPrimaryNavigationFragment()) {
return;
}

// We need to hide the current showing fragment (primary fragment)
final Fragment currentShowingFragment = getSupportFragmentManager().getPrimaryNavigationFragment();

final FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
if (currentShowingFragment != null) {
fragmentTransaction.hide(currentShowingFragment);
}

// We try to find that fragment if it was already added before
final Fragment alreadyAddedFragment = getSupportFragmentManager().findFragmentByTag(tag);
if (alreadyAddedFragment != null) {
// Since its already added before we just set it as primary navigation and show it again
fragmentTransaction.setPrimaryNavigationFragment(alreadyAddedFragment);
fragmentTransaction.show(alreadyAddedFragment);
} else {
// We add the new fragment and then show it
fragmentTransaction.add(containerId, fragment, tag);
fragmentTransaction.show(fragment);
// We set it as the primary navigation to support back stack and back navigation
fragmentTransaction.setPrimaryNavigationFragment(fragment);
}

fragmentTransaction.commit();
}





share|improve this answer































    2














    If you have a bottomNavigationView with 3 items corresponding to 3 Fragments: FragmentA, FragmentB and FragmentC where FragmentA is the startDestination in your navigation graph, then when you're on FragmentB or FragmentC and you click back, you're going to be redirected to FragmentA, that's the behavior recommended by Google and that's implemented by default.



    If however you wish to alter this behavior, you'll need to either use a ViewPager as was suggested by some of the other answers, or manually handle the fragments backStack and back transactions yourself -which in a way would undermine the use of the Navigation component altogether-.






    share|improve this answer



















    • 3





      But youtube, Instagram, Saavn has different behaviour

      – Bincy Baby
      Aug 29 '18 at 2:05











    • True, there's no right or wrong way of doing it, it's just about what google supports by default (and thus recommends) and what your needs are. If these two don't align you need to work around it.

      – Husayn Hakeem
      Aug 30 '18 at 18:26











    • But the problem is that if you are using the JetPack Navigation the backStack will be empty. Apparently JetPack is not adding nothing to the back stack when handling BottomNavigation clicks.

      – Allan Veloso
      Jan 30 at 23:12



















    0














    First, let me clarify how Youtube and Instagram handles fragment navigation.




    • When the user is on a detail fragment, back or up pop the stack once, with the state properly restaured. A second click on the already selected bottom bar item pop all the stack to the root, refreshing it

    • When the user is on a root fragment, back goes to the last menu selected on the bottom bar, displaying the last detail fragment, with the state properly restaured (JetPack doesn't)

    • When the user is on the start destination fragment, back finishes activity


    None of the other answers above solve all this problems using the jetpack navigation.



    JetPack navigation has no standard way to do this, the way that I found more simple is to dividing the navigation xml graph into one for each bottom navigation item, handling the back stack between the navigation items myself using the activity FragmentManager and use the JetPack NavController to handle the internal navigation between root and detail fragments (its implementation uses the childFragmentManager stack).



    Suppose you have in your navigation folder this 3 xmls:



    res/navigation/
    navigation_feed.xml
    navigation_explore.xml
    navigation_profile.xml


    Have your destinationIds inside the navigation xmls the same of your bottomNavigationBar menu ids. Also, to each xml set the app:startDestination to the fragment that you want as the root of the navigation item.



    Create a class BottomNavController.kt:



    class BottomNavController(
    val context: Context,
    @IdRes val containerId: Int,
    @IdRes val appStartDestinationId: Int
    ) {
    private val navigationBackStack = BackStack.of(appStartDestinationId)
    lateinit var activity: Activity
    lateinit var fragmentManager: FragmentManager
    private var listener: OnNavigationItemChanged? = null
    private var navGraphProvider: NavGraphProvider? = null

    interface OnNavigationItemChanged {
    fun onItemChanged(itemId: Int)
    }

    interface NavGraphProvider {
    @NavigationRes
    fun getNavGraphId(itemId: Int): Int
    }

    init {
    var ctx = context
    while (ctx is ContextWrapper) {
    if (ctx is Activity) {
    activity = ctx
    fragmentManager = (activity as FragmentActivity).supportFragmentManager
    break
    }
    ctx = ctx.baseContext
    }
    }

    fun setOnItemNavigationChanged(listener: (itemId: Int) -> Unit) {
    this.listener = object : OnNavigationItemChanged {
    override fun onItemChanged(itemId: Int) {
    listener.invoke(itemId)
    }
    }
    }

    fun setNavGraphProvider(provider: NavGraphProvider) {
    navGraphProvider = provider
    }

    fun onNavigationItemReselected(item: MenuItem) {
    // If the user press a second time the navigation button, we pop the back stack to the root
    activity.findNavController(containerId).popBackStack(item.itemId, false)
    }

    fun onNavigationItemSelected(itemId: Int = navigationBackStack.last()): Boolean {

    // Replace fragment representing a navigation item
    val fragment = fragmentManager.findFragmentByTag(itemId.toString())
    ?: NavHostFragment.create(navGraphProvider?.getNavGraphId(itemId)
    ?: throw RuntimeException("You need to set up a NavGraphProvider with " +
    "BottomNavController#setNavGraphProvider")
    )
    fragmentManager.beginTransaction()
    .setCustomAnimations(
    R.anim.nav_default_enter_anim,
    R.anim.nav_default_exit_anim,
    R.anim.nav_default_pop_enter_anim,
    R.anim.nav_default_pop_exit_anim
    )
    .replace(containerId, fragment, itemId.toString())
    .addToBackStack(null)
    .commit()

    // Add to back stack
    navigationBackStack.moveLast(itemId)

    listener?.onItemChanged(itemId)

    return true
    }

    fun onBackPressed() {
    val childFragmentManager = fragmentManager.findFragmentById(containerId)!!
    .childFragmentManager
    when {
    // We should always try to go back on the child fragment manager stack before going to
    // the navigation stack. It's important to use the child fragment manager instead of the
    // NavController because if the user change tabs super fast commit of the
    // supportFragmentManager may mess up with the NavController child fragment manager back
    // stack
    childFragmentManager.popBackStackImmediate() -> {
    }
    // Fragment back stack is empty so try to go back on the navigation stack
    navigationBackStack.size > 1 -> {
    // Remove last item from back stack
    navigationBackStack.removeLast()

    // Update the container with new fragment
    onNavigationItemSelected()
    }
    // If the stack has only one and it's not the navigation home we should
    // ensure that the application always leave from startDestination
    navigationBackStack.last() != appStartDestinationId -> {
    navigationBackStack.removeLast()
    navigationBackStack.add(0, appStartDestinationId)
    onNavigationItemSelected()
    }
    // Navigation stack is empty, so finish the activity
    else -> activity.finish()
    }
    }

    private class BackStack : ArrayList<Int>() {
    companion object {
    fun of(vararg elements: Int): BackStack {
    val b = BackStack()
    b.addAll(elements.toTypedArray())
    return b
    }
    }

    fun removeLast() = removeAt(size - 1)
    fun moveLast(item: Int) {
    remove(item)
    add(item)
    }
    }
    }

    // Convenience extension to set up the navigation
    fun BottomNavigationView.setUpNavigation(bottomNavController: BottomNavController, onReselect: ((menuItem: MenuItem) -> Unit)? = null) {
    setOnNavigationItemSelectedListener {
    bottomNavController.onNavigationItemSelected(it.itemId)
    }
    setOnNavigationItemReselectedListener {
    bottomNavController.onNavigationItemReselected(it)
    onReselect?.invoke(it)
    }
    bottomNavController.setOnItemNavigationChanged { itemId ->
    menu.findItem(itemId).isChecked = true
    }
    }


    Do your layout main.xml like this:



    <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">

    <FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
    app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottomNavigationView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginStart="0dp"
    android:layout_marginEnd="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:menu="@menu/navigation" />

    </androidx.constraintlayout.widget.ConstraintLayout>


    Use on your activity like this:



    class MainActivity : AppCompatActivity(),
    BottomNavController.NavGraphProvider {

    private val navController by lazy(LazyThreadSafetyMode.NONE) {
    Navigation.findNavController(this, R.id.container)
    }

    private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) {
    BottomNavController(this, R.id.container, R.id.navigation_feed)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)

    bottomNavController.setNavGraphProvider(this)
    bottomNavigationView.setUpNavigation(bottomNavController)
    if (savedInstanceState == null) bottomNavController
    .onNavigationItemSelected()

    // do your things...
    }

    override fun getNavGraphId(itemId: Int) = when (itemId) {
    R.id.navigation_feed -> R.navigation.navigation_feed
    R.id.navigation_explore -> R.navigation.navigation_explore
    R.id.navigation_profile -> R.navigation.navigation_profile
    else -> R.navigation.navigation_feed
    }

    override fun onSupportNavigateUp(): Boolean = navController
    .navigateUp()

    override fun onBackPressed() = bottomNavController.onBackPressed()
    }





    share|improve this answer


























    • this solution looks good but there are some things i noticed : <FrameLayout /> should be a NavHostFragment, every graph got it's own home default so doing this if (savedInstanceState == null) bottomNavController .onNavigationItemSelected() will trigger the fragment two times , it doesn't hold states for the fragments.

      – Sim
      Mar 4 at 6:46











    • The idea of the saved instance state exactly avoided the fragment be created two times. I can't check this because I ended up extending the NavController, and creating a custom Navigator exclusively for NavHostFragments, adding it to this NavController (I called NavHostFragmentNavController). Then I create a graph called navigation_main.xml with the <nav-fragment> elements that represent each a bottom navigation item. The new implementations are bigger but the usage quite simple. The code still has some small bugs that I did not finish yet. I will post it when I fix them.

      – Allan Veloso
      Mar 4 at 14:52











    • yes i guess for now extending the NavController is a sane solution, till Google release the sample.

      – Sim
      Mar 5 at 7:18











    • Shouldn't here a destination be passed as the last parameter instead of R.id.navigation_feed? private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) { BottomNavController(this, R.id.container, R.id.navigation_feed) }

      – WWJD
      Mar 5 at 10:19






    • 1





      @WWJD, R.id.navigation_feed is a destination. I named the graph id with the same name as it's an initial destination, so R.navigation.navigation_feed has a R.id.navigation_feed destination.

      – Allan Veloso
      Mar 5 at 14:09











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f50577356%2fandroid-jetpack-navigation-bottomnavigationview-with-youtube-or-instagram-like%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    6 Answers
    6






    active

    oldest

    votes








    6 Answers
    6






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    26














    You don't really need a ViewPager to work with BottomNavigation and the new Navigation architecture component. I have been working in a sample app that uses exactly the two, see here.



    The basic concept is this, you have the main activity that will host the BottomNavigationView and that is the Navigation host for your navigation graph, this is how the xml for it look like:



    activity_main.xml



    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".main.MainActivity">

    <fragment
    android:id="@+id/my_nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:defaultNavHost="true"
    app:layout_constraintBottom_toTopOf="@+id/navigation"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="0dp"
    android:layout_marginEnd="0dp"
    android:background="?android:attr/windowBackground"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:menu="@menu/navigation" />

    </android.support.constraint.ConstraintLayout>


    The navigation Menu (tabs menu) for the BottomNavigationView looks like this:



    navigation.xml



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
    android:id="@+id/navigation_home"
    android:icon="@drawable/ic_home"
    android:title="@string/title_home" />

    <item
    android:id="@+id/navigation_people"
    android:icon="@drawable/ic_group"
    android:title="@string/title_people" />

    <item
    android:id="@+id/navigation_organization"
    android:icon="@drawable/ic_organization"
    android:title="@string/title_organization" />

    <item
    android:id="@+id/navigation_business"
    android:icon="@drawable/ic_business"
    android:title="@string/title_business" />

    <item
    android:id="@+id/navigation_tasks"
    android:icon="@drawable/ic_dashboard"
    android:title="@string/title_tasks" />

    </menu>


    All of this is just the BottomNavigationView setup. Now to make it work with the Navigation Arch Component you need to go into the navigation graph editor, add all your fragment destinations (in my case I have 5 of them, one for each tab) and set the id of the destination with the same name as the one in the navigation.xml file:



    enter image description here



    This will tell android to make a link between the tab and the fragment, now every time the user clicks the "Home" tab android will take care of loading up the correct fragment.
    There is also one piece of kotlin code that needs to be added to your NavHost (the main activity) to wire things up with the BottomNavigationView:



    You need to add in your onCreate:



    bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


    This tells android to do the wiring between the Navigation architecture component and the BottomNavigationView. See more in the docs.



    To get the same beahvior you have when you use youtube, just add this:



    navigation.setOnNavigationItemSelectedListener {item ->

    onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

    }


    This will make destinations go into the backstack so when you hit the back button, the last visited destination will be popped up.






    share|improve this answer





















    • 3





      The secret sauce was adding the id in the nav graph. I'm using Navigation Drawer, but the principal is the same

      – JamesSugrue
      May 31 '18 at 23:21






    • 3





      but back navigation is not working properly

      – Bincy Baby
      Jun 1 '18 at 18:04











    • Can we have single instance of fragment ?

      – Bincy Baby
      Jun 2 '18 at 10:12






    • 10





      This is working fine with the back-button. But if user click on bottom tabs its not restoring the previously open child-fragment of that tab(if available). It just opening the new instant of (parent) fragment every time user click on bottom tabs. So this way will lead to a confusing/ frustrating nevigation experience to the users if navigated using bottom tabs many times. Dangerous implementation

      – Niroshan
      Jun 25 '18 at 5:26






    • 2





      There's no max number, but material design rules state you should only have 5 tabs max

      – Levi Moreira
      Sep 30 '18 at 20:36
















    26














    You don't really need a ViewPager to work with BottomNavigation and the new Navigation architecture component. I have been working in a sample app that uses exactly the two, see here.



    The basic concept is this, you have the main activity that will host the BottomNavigationView and that is the Navigation host for your navigation graph, this is how the xml for it look like:



    activity_main.xml



    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".main.MainActivity">

    <fragment
    android:id="@+id/my_nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:defaultNavHost="true"
    app:layout_constraintBottom_toTopOf="@+id/navigation"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="0dp"
    android:layout_marginEnd="0dp"
    android:background="?android:attr/windowBackground"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:menu="@menu/navigation" />

    </android.support.constraint.ConstraintLayout>


    The navigation Menu (tabs menu) for the BottomNavigationView looks like this:



    navigation.xml



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
    android:id="@+id/navigation_home"
    android:icon="@drawable/ic_home"
    android:title="@string/title_home" />

    <item
    android:id="@+id/navigation_people"
    android:icon="@drawable/ic_group"
    android:title="@string/title_people" />

    <item
    android:id="@+id/navigation_organization"
    android:icon="@drawable/ic_organization"
    android:title="@string/title_organization" />

    <item
    android:id="@+id/navigation_business"
    android:icon="@drawable/ic_business"
    android:title="@string/title_business" />

    <item
    android:id="@+id/navigation_tasks"
    android:icon="@drawable/ic_dashboard"
    android:title="@string/title_tasks" />

    </menu>


    All of this is just the BottomNavigationView setup. Now to make it work with the Navigation Arch Component you need to go into the navigation graph editor, add all your fragment destinations (in my case I have 5 of them, one for each tab) and set the id of the destination with the same name as the one in the navigation.xml file:



    enter image description here



    This will tell android to make a link between the tab and the fragment, now every time the user clicks the "Home" tab android will take care of loading up the correct fragment.
    There is also one piece of kotlin code that needs to be added to your NavHost (the main activity) to wire things up with the BottomNavigationView:



    You need to add in your onCreate:



    bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


    This tells android to do the wiring between the Navigation architecture component and the BottomNavigationView. See more in the docs.



    To get the same beahvior you have when you use youtube, just add this:



    navigation.setOnNavigationItemSelectedListener {item ->

    onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

    }


    This will make destinations go into the backstack so when you hit the back button, the last visited destination will be popped up.






    share|improve this answer





















    • 3





      The secret sauce was adding the id in the nav graph. I'm using Navigation Drawer, but the principal is the same

      – JamesSugrue
      May 31 '18 at 23:21






    • 3





      but back navigation is not working properly

      – Bincy Baby
      Jun 1 '18 at 18:04











    • Can we have single instance of fragment ?

      – Bincy Baby
      Jun 2 '18 at 10:12






    • 10





      This is working fine with the back-button. But if user click on bottom tabs its not restoring the previously open child-fragment of that tab(if available). It just opening the new instant of (parent) fragment every time user click on bottom tabs. So this way will lead to a confusing/ frustrating nevigation experience to the users if navigated using bottom tabs many times. Dangerous implementation

      – Niroshan
      Jun 25 '18 at 5:26






    • 2





      There's no max number, but material design rules state you should only have 5 tabs max

      – Levi Moreira
      Sep 30 '18 at 20:36














    26












    26








    26







    You don't really need a ViewPager to work with BottomNavigation and the new Navigation architecture component. I have been working in a sample app that uses exactly the two, see here.



    The basic concept is this, you have the main activity that will host the BottomNavigationView and that is the Navigation host for your navigation graph, this is how the xml for it look like:



    activity_main.xml



    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".main.MainActivity">

    <fragment
    android:id="@+id/my_nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:defaultNavHost="true"
    app:layout_constraintBottom_toTopOf="@+id/navigation"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="0dp"
    android:layout_marginEnd="0dp"
    android:background="?android:attr/windowBackground"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:menu="@menu/navigation" />

    </android.support.constraint.ConstraintLayout>


    The navigation Menu (tabs menu) for the BottomNavigationView looks like this:



    navigation.xml



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
    android:id="@+id/navigation_home"
    android:icon="@drawable/ic_home"
    android:title="@string/title_home" />

    <item
    android:id="@+id/navigation_people"
    android:icon="@drawable/ic_group"
    android:title="@string/title_people" />

    <item
    android:id="@+id/navigation_organization"
    android:icon="@drawable/ic_organization"
    android:title="@string/title_organization" />

    <item
    android:id="@+id/navigation_business"
    android:icon="@drawable/ic_business"
    android:title="@string/title_business" />

    <item
    android:id="@+id/navigation_tasks"
    android:icon="@drawable/ic_dashboard"
    android:title="@string/title_tasks" />

    </menu>


    All of this is just the BottomNavigationView setup. Now to make it work with the Navigation Arch Component you need to go into the navigation graph editor, add all your fragment destinations (in my case I have 5 of them, one for each tab) and set the id of the destination with the same name as the one in the navigation.xml file:



    enter image description here



    This will tell android to make a link between the tab and the fragment, now every time the user clicks the "Home" tab android will take care of loading up the correct fragment.
    There is also one piece of kotlin code that needs to be added to your NavHost (the main activity) to wire things up with the BottomNavigationView:



    You need to add in your onCreate:



    bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


    This tells android to do the wiring between the Navigation architecture component and the BottomNavigationView. See more in the docs.



    To get the same beahvior you have when you use youtube, just add this:



    navigation.setOnNavigationItemSelectedListener {item ->

    onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

    }


    This will make destinations go into the backstack so when you hit the back button, the last visited destination will be popped up.






    share|improve this answer















    You don't really need a ViewPager to work with BottomNavigation and the new Navigation architecture component. I have been working in a sample app that uses exactly the two, see here.



    The basic concept is this, you have the main activity that will host the BottomNavigationView and that is the Navigation host for your navigation graph, this is how the xml for it look like:



    activity_main.xml



    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".main.MainActivity">

    <fragment
    android:id="@+id/my_nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:defaultNavHost="true"
    app:layout_constraintBottom_toTopOf="@+id/navigation"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="0dp"
    android:layout_marginEnd="0dp"
    android:background="?android:attr/windowBackground"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:menu="@menu/navigation" />

    </android.support.constraint.ConstraintLayout>


    The navigation Menu (tabs menu) for the BottomNavigationView looks like this:



    navigation.xml



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
    android:id="@+id/navigation_home"
    android:icon="@drawable/ic_home"
    android:title="@string/title_home" />

    <item
    android:id="@+id/navigation_people"
    android:icon="@drawable/ic_group"
    android:title="@string/title_people" />

    <item
    android:id="@+id/navigation_organization"
    android:icon="@drawable/ic_organization"
    android:title="@string/title_organization" />

    <item
    android:id="@+id/navigation_business"
    android:icon="@drawable/ic_business"
    android:title="@string/title_business" />

    <item
    android:id="@+id/navigation_tasks"
    android:icon="@drawable/ic_dashboard"
    android:title="@string/title_tasks" />

    </menu>


    All of this is just the BottomNavigationView setup. Now to make it work with the Navigation Arch Component you need to go into the navigation graph editor, add all your fragment destinations (in my case I have 5 of them, one for each tab) and set the id of the destination with the same name as the one in the navigation.xml file:



    enter image description here



    This will tell android to make a link between the tab and the fragment, now every time the user clicks the "Home" tab android will take care of loading up the correct fragment.
    There is also one piece of kotlin code that needs to be added to your NavHost (the main activity) to wire things up with the BottomNavigationView:



    You need to add in your onCreate:



    bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))


    This tells android to do the wiring between the Navigation architecture component and the BottomNavigationView. See more in the docs.



    To get the same beahvior you have when you use youtube, just add this:



    navigation.setOnNavigationItemSelectedListener {item ->

    onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

    }


    This will make destinations go into the backstack so when you hit the back button, the last visited destination will be popped up.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jun 1 '18 at 18:43

























    answered May 31 '18 at 14:18









    Levi MoreiraLevi Moreira

    8,44731333




    8,44731333








    • 3





      The secret sauce was adding the id in the nav graph. I'm using Navigation Drawer, but the principal is the same

      – JamesSugrue
      May 31 '18 at 23:21






    • 3





      but back navigation is not working properly

      – Bincy Baby
      Jun 1 '18 at 18:04











    • Can we have single instance of fragment ?

      – Bincy Baby
      Jun 2 '18 at 10:12






    • 10





      This is working fine with the back-button. But if user click on bottom tabs its not restoring the previously open child-fragment of that tab(if available). It just opening the new instant of (parent) fragment every time user click on bottom tabs. So this way will lead to a confusing/ frustrating nevigation experience to the users if navigated using bottom tabs many times. Dangerous implementation

      – Niroshan
      Jun 25 '18 at 5:26






    • 2





      There's no max number, but material design rules state you should only have 5 tabs max

      – Levi Moreira
      Sep 30 '18 at 20:36














    • 3





      The secret sauce was adding the id in the nav graph. I'm using Navigation Drawer, but the principal is the same

      – JamesSugrue
      May 31 '18 at 23:21






    • 3





      but back navigation is not working properly

      – Bincy Baby
      Jun 1 '18 at 18:04











    • Can we have single instance of fragment ?

      – Bincy Baby
      Jun 2 '18 at 10:12






    • 10





      This is working fine with the back-button. But if user click on bottom tabs its not restoring the previously open child-fragment of that tab(if available). It just opening the new instant of (parent) fragment every time user click on bottom tabs. So this way will lead to a confusing/ frustrating nevigation experience to the users if navigated using bottom tabs many times. Dangerous implementation

      – Niroshan
      Jun 25 '18 at 5:26






    • 2





      There's no max number, but material design rules state you should only have 5 tabs max

      – Levi Moreira
      Sep 30 '18 at 20:36








    3




    3





    The secret sauce was adding the id in the nav graph. I'm using Navigation Drawer, but the principal is the same

    – JamesSugrue
    May 31 '18 at 23:21





    The secret sauce was adding the id in the nav graph. I'm using Navigation Drawer, but the principal is the same

    – JamesSugrue
    May 31 '18 at 23:21




    3




    3





    but back navigation is not working properly

    – Bincy Baby
    Jun 1 '18 at 18:04





    but back navigation is not working properly

    – Bincy Baby
    Jun 1 '18 at 18:04













    Can we have single instance of fragment ?

    – Bincy Baby
    Jun 2 '18 at 10:12





    Can we have single instance of fragment ?

    – Bincy Baby
    Jun 2 '18 at 10:12




    10




    10





    This is working fine with the back-button. But if user click on bottom tabs its not restoring the previously open child-fragment of that tab(if available). It just opening the new instant of (parent) fragment every time user click on bottom tabs. So this way will lead to a confusing/ frustrating nevigation experience to the users if navigated using bottom tabs many times. Dangerous implementation

    – Niroshan
    Jun 25 '18 at 5:26





    This is working fine with the back-button. But if user click on bottom tabs its not restoring the previously open child-fragment of that tab(if available). It just opening the new instant of (parent) fragment every time user click on bottom tabs. So this way will lead to a confusing/ frustrating nevigation experience to the users if navigated using bottom tabs many times. Dangerous implementation

    – Niroshan
    Jun 25 '18 at 5:26




    2




    2





    There's no max number, but material design rules state you should only have 5 tabs max

    – Levi Moreira
    Sep 30 '18 at 20:36





    There's no max number, but material design rules state you should only have 5 tabs max

    – Levi Moreira
    Sep 30 '18 at 20:36













    15














    You have to set host navigation like below xml:



    <LinearLayout 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"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary" />

    <fragment
    android:id="@+id/navigation_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/bottom_navigation_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:itemIconTint="@drawable/color_state_list"
    app:itemTextColor="@drawable/color_state_list"
    app:menu="@menu/menu_bottom_navigation" />
    </LinearLayout>


    Setup With Navigation Controller :



    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_host_fragment);
    NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());


    menu_bottom_navigation.xml :



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
    android:id="@id/tab1" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab1" />
    <item
    android:id="@id/tab2" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab2" />

    <item
    android:id="@id/tab3" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab3" />
    </menu>


    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/tab1">
    <fragment
    android:id="@+id/tab1"
    android:name="com.navigationsample.Tab1Fragment"
    android:label="@string/tab1"
    tools:layout="@layout/fragment_tab_1" />

    <fragment
    android:id="@+id/tab2"
    android:name="com.navigationsample.Tab2Fragment"
    android:label="@string/tab2"
    tools:layout="@layout/fragment_tab_2"/>

    <fragment
    android:id="@+id/tab3"
    android:name="com.simform.navigationsample.Tab3Fragment"
    android:label="@string/tab3"
    tools:layout="@layout/fragment_tab_3"/>
    </navigation>


    By setting up the same id of "nav_graph" to "menu_bottom_navigation" will handle the click of bottom navigation.



    You can handle back action using popUpTo property in action tag.
    enter image description here






    share|improve this answer


























    • can you elaborate use of popUpTo ?

      – Bincy Baby
      Aug 12 '18 at 10:49











    • @BincyBaby popUpTo property helps you to return on particular fragment on back press.

      – SANAT
      Aug 18 '18 at 15:51






    • 1





      @SANAT but how to set up the popUpTo to the fragment imediatelly pressed before? Like, if you were in frag1 went to frag2 and then to frag3, back press should go back to frag2. If you were in frag1 and went directly to frag3, back press sould go back to frag1. The popUpTo seems to only let you choose one fragment to go back idependently of the user path.

      – Allan Veloso
      Jan 30 at 22:57
















    15














    You have to set host navigation like below xml:



    <LinearLayout 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"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary" />

    <fragment
    android:id="@+id/navigation_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/bottom_navigation_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:itemIconTint="@drawable/color_state_list"
    app:itemTextColor="@drawable/color_state_list"
    app:menu="@menu/menu_bottom_navigation" />
    </LinearLayout>


    Setup With Navigation Controller :



    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_host_fragment);
    NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());


    menu_bottom_navigation.xml :



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
    android:id="@id/tab1" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab1" />
    <item
    android:id="@id/tab2" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab2" />

    <item
    android:id="@id/tab3" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab3" />
    </menu>


    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/tab1">
    <fragment
    android:id="@+id/tab1"
    android:name="com.navigationsample.Tab1Fragment"
    android:label="@string/tab1"
    tools:layout="@layout/fragment_tab_1" />

    <fragment
    android:id="@+id/tab2"
    android:name="com.navigationsample.Tab2Fragment"
    android:label="@string/tab2"
    tools:layout="@layout/fragment_tab_2"/>

    <fragment
    android:id="@+id/tab3"
    android:name="com.simform.navigationsample.Tab3Fragment"
    android:label="@string/tab3"
    tools:layout="@layout/fragment_tab_3"/>
    </navigation>


    By setting up the same id of "nav_graph" to "menu_bottom_navigation" will handle the click of bottom navigation.



    You can handle back action using popUpTo property in action tag.
    enter image description here






    share|improve this answer


























    • can you elaborate use of popUpTo ?

      – Bincy Baby
      Aug 12 '18 at 10:49











    • @BincyBaby popUpTo property helps you to return on particular fragment on back press.

      – SANAT
      Aug 18 '18 at 15:51






    • 1





      @SANAT but how to set up the popUpTo to the fragment imediatelly pressed before? Like, if you were in frag1 went to frag2 and then to frag3, back press should go back to frag2. If you were in frag1 and went directly to frag3, back press sould go back to frag1. The popUpTo seems to only let you choose one fragment to go back idependently of the user path.

      – Allan Veloso
      Jan 30 at 22:57














    15












    15








    15







    You have to set host navigation like below xml:



    <LinearLayout 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"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary" />

    <fragment
    android:id="@+id/navigation_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/bottom_navigation_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:itemIconTint="@drawable/color_state_list"
    app:itemTextColor="@drawable/color_state_list"
    app:menu="@menu/menu_bottom_navigation" />
    </LinearLayout>


    Setup With Navigation Controller :



    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_host_fragment);
    NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());


    menu_bottom_navigation.xml :



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
    android:id="@id/tab1" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab1" />
    <item
    android:id="@id/tab2" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab2" />

    <item
    android:id="@id/tab3" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab3" />
    </menu>


    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/tab1">
    <fragment
    android:id="@+id/tab1"
    android:name="com.navigationsample.Tab1Fragment"
    android:label="@string/tab1"
    tools:layout="@layout/fragment_tab_1" />

    <fragment
    android:id="@+id/tab2"
    android:name="com.navigationsample.Tab2Fragment"
    android:label="@string/tab2"
    tools:layout="@layout/fragment_tab_2"/>

    <fragment
    android:id="@+id/tab3"
    android:name="com.simform.navigationsample.Tab3Fragment"
    android:label="@string/tab3"
    tools:layout="@layout/fragment_tab_3"/>
    </navigation>


    By setting up the same id of "nav_graph" to "menu_bottom_navigation" will handle the click of bottom navigation.



    You can handle back action using popUpTo property in action tag.
    enter image description here






    share|improve this answer















    You have to set host navigation like below xml:



    <LinearLayout 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"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary" />

    <fragment
    android:id="@+id/navigation_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
    android:id="@+id/bottom_navigation_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:itemIconTint="@drawable/color_state_list"
    app:itemTextColor="@drawable/color_state_list"
    app:menu="@menu/menu_bottom_navigation" />
    </LinearLayout>


    Setup With Navigation Controller :



    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_host_fragment);
    NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.getNavController());


    menu_bottom_navigation.xml :



    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
    android:id="@id/tab1" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab1" />
    <item
    android:id="@id/tab2" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab2" />

    <item
    android:id="@id/tab3" // Id of navigation graph
    android:icon="@mipmap/ic_launcher"
    android:title="@string/tab3" />
    </menu>


    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/tab1">
    <fragment
    android:id="@+id/tab1"
    android:name="com.navigationsample.Tab1Fragment"
    android:label="@string/tab1"
    tools:layout="@layout/fragment_tab_1" />

    <fragment
    android:id="@+id/tab2"
    android:name="com.navigationsample.Tab2Fragment"
    android:label="@string/tab2"
    tools:layout="@layout/fragment_tab_2"/>

    <fragment
    android:id="@+id/tab3"
    android:name="com.simform.navigationsample.Tab3Fragment"
    android:label="@string/tab3"
    tools:layout="@layout/fragment_tab_3"/>
    </navigation>


    By setting up the same id of "nav_graph" to "menu_bottom_navigation" will handle the click of bottom navigation.



    You can handle back action using popUpTo property in action tag.
    enter image description here







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jun 20 '18 at 12:34

























    answered Jun 20 '18 at 12:27









    SANATSANAT

    3,9592643




    3,9592643













    • can you elaborate use of popUpTo ?

      – Bincy Baby
      Aug 12 '18 at 10:49











    • @BincyBaby popUpTo property helps you to return on particular fragment on back press.

      – SANAT
      Aug 18 '18 at 15:51






    • 1





      @SANAT but how to set up the popUpTo to the fragment imediatelly pressed before? Like, if you were in frag1 went to frag2 and then to frag3, back press should go back to frag2. If you were in frag1 and went directly to frag3, back press sould go back to frag1. The popUpTo seems to only let you choose one fragment to go back idependently of the user path.

      – Allan Veloso
      Jan 30 at 22:57



















    • can you elaborate use of popUpTo ?

      – Bincy Baby
      Aug 12 '18 at 10:49











    • @BincyBaby popUpTo property helps you to return on particular fragment on back press.

      – SANAT
      Aug 18 '18 at 15:51






    • 1





      @SANAT but how to set up the popUpTo to the fragment imediatelly pressed before? Like, if you were in frag1 went to frag2 and then to frag3, back press should go back to frag2. If you were in frag1 and went directly to frag3, back press sould go back to frag1. The popUpTo seems to only let you choose one fragment to go back idependently of the user path.

      – Allan Veloso
      Jan 30 at 22:57

















    can you elaborate use of popUpTo ?

    – Bincy Baby
    Aug 12 '18 at 10:49





    can you elaborate use of popUpTo ?

    – Bincy Baby
    Aug 12 '18 at 10:49













    @BincyBaby popUpTo property helps you to return on particular fragment on back press.

    – SANAT
    Aug 18 '18 at 15:51





    @BincyBaby popUpTo property helps you to return on particular fragment on back press.

    – SANAT
    Aug 18 '18 at 15:51




    1




    1





    @SANAT but how to set up the popUpTo to the fragment imediatelly pressed before? Like, if you were in frag1 went to frag2 and then to frag3, back press should go back to frag2. If you were in frag1 and went directly to frag3, back press sould go back to frag1. The popUpTo seems to only let you choose one fragment to go back idependently of the user path.

    – Allan Veloso
    Jan 30 at 22:57





    @SANAT but how to set up the popUpTo to the fragment imediatelly pressed before? Like, if you were in frag1 went to frag2 and then to frag3, back press should go back to frag2. If you were in frag1 and went directly to frag3, back press sould go back to frag1. The popUpTo seems to only let you choose one fragment to go back idependently of the user path.

    – Allan Veloso
    Jan 30 at 22:57











    8














    You can have a viewpager setup with bottom navigation view. Each fragment in the viewpager will be a container fragment, it will have child fragments with its own backstack. You can maintain backstack for each tab in viewpager this way






    share|improve this answer
























    • I was using that way, but app starting takes too much time to first launch

      – Bincy Baby
      May 29 '18 at 6:16











    • Then you must be doing something wrong, make sure you are not doing some heavy work in the oncreate or oncreateview of the fragments. There is no way it would take time

      – Suhaib Roomy
      May 29 '18 at 6:21











    • I have to load contents , i don't think youtube or instagram used ViewPager

      – Bincy Baby
      May 29 '18 at 6:48











    • Its definitely a viewpager. Just scroll on one page and try switiching tabs, its really fast and it resumes from the same state. There is no way you can achieve it by changing fragments on the same container, these are multiple fragments viewed using a viewpager

      – Suhaib Roomy
      May 29 '18 at 9:28











    • My guess is also that YouTube or Instagram do not use ViewPager. The restoring happens because of the backStack pop action that resumes the underlying fragment that is added in the first place not replaced

      – HawkPriest
      Sep 6 '18 at 14:13
















    8














    You can have a viewpager setup with bottom navigation view. Each fragment in the viewpager will be a container fragment, it will have child fragments with its own backstack. You can maintain backstack for each tab in viewpager this way






    share|improve this answer
























    • I was using that way, but app starting takes too much time to first launch

      – Bincy Baby
      May 29 '18 at 6:16











    • Then you must be doing something wrong, make sure you are not doing some heavy work in the oncreate or oncreateview of the fragments. There is no way it would take time

      – Suhaib Roomy
      May 29 '18 at 6:21











    • I have to load contents , i don't think youtube or instagram used ViewPager

      – Bincy Baby
      May 29 '18 at 6:48











    • Its definitely a viewpager. Just scroll on one page and try switiching tabs, its really fast and it resumes from the same state. There is no way you can achieve it by changing fragments on the same container, these are multiple fragments viewed using a viewpager

      – Suhaib Roomy
      May 29 '18 at 9:28











    • My guess is also that YouTube or Instagram do not use ViewPager. The restoring happens because of the backStack pop action that resumes the underlying fragment that is added in the first place not replaced

      – HawkPriest
      Sep 6 '18 at 14:13














    8












    8








    8







    You can have a viewpager setup with bottom navigation view. Each fragment in the viewpager will be a container fragment, it will have child fragments with its own backstack. You can maintain backstack for each tab in viewpager this way






    share|improve this answer













    You can have a viewpager setup with bottom navigation view. Each fragment in the viewpager will be a container fragment, it will have child fragments with its own backstack. You can maintain backstack for each tab in viewpager this way







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered May 29 '18 at 6:11









    Suhaib RoomySuhaib Roomy

    1,3511616




    1,3511616













    • I was using that way, but app starting takes too much time to first launch

      – Bincy Baby
      May 29 '18 at 6:16











    • Then you must be doing something wrong, make sure you are not doing some heavy work in the oncreate or oncreateview of the fragments. There is no way it would take time

      – Suhaib Roomy
      May 29 '18 at 6:21











    • I have to load contents , i don't think youtube or instagram used ViewPager

      – Bincy Baby
      May 29 '18 at 6:48











    • Its definitely a viewpager. Just scroll on one page and try switiching tabs, its really fast and it resumes from the same state. There is no way you can achieve it by changing fragments on the same container, these are multiple fragments viewed using a viewpager

      – Suhaib Roomy
      May 29 '18 at 9:28











    • My guess is also that YouTube or Instagram do not use ViewPager. The restoring happens because of the backStack pop action that resumes the underlying fragment that is added in the first place not replaced

      – HawkPriest
      Sep 6 '18 at 14:13



















    • I was using that way, but app starting takes too much time to first launch

      – Bincy Baby
      May 29 '18 at 6:16











    • Then you must be doing something wrong, make sure you are not doing some heavy work in the oncreate or oncreateview of the fragments. There is no way it would take time

      – Suhaib Roomy
      May 29 '18 at 6:21











    • I have to load contents , i don't think youtube or instagram used ViewPager

      – Bincy Baby
      May 29 '18 at 6:48











    • Its definitely a viewpager. Just scroll on one page and try switiching tabs, its really fast and it resumes from the same state. There is no way you can achieve it by changing fragments on the same container, these are multiple fragments viewed using a viewpager

      – Suhaib Roomy
      May 29 '18 at 9:28











    • My guess is also that YouTube or Instagram do not use ViewPager. The restoring happens because of the backStack pop action that resumes the underlying fragment that is added in the first place not replaced

      – HawkPriest
      Sep 6 '18 at 14:13

















    I was using that way, but app starting takes too much time to first launch

    – Bincy Baby
    May 29 '18 at 6:16





    I was using that way, but app starting takes too much time to first launch

    – Bincy Baby
    May 29 '18 at 6:16













    Then you must be doing something wrong, make sure you are not doing some heavy work in the oncreate or oncreateview of the fragments. There is no way it would take time

    – Suhaib Roomy
    May 29 '18 at 6:21





    Then you must be doing something wrong, make sure you are not doing some heavy work in the oncreate or oncreateview of the fragments. There is no way it would take time

    – Suhaib Roomy
    May 29 '18 at 6:21













    I have to load contents , i don't think youtube or instagram used ViewPager

    – Bincy Baby
    May 29 '18 at 6:48





    I have to load contents , i don't think youtube or instagram used ViewPager

    – Bincy Baby
    May 29 '18 at 6:48













    Its definitely a viewpager. Just scroll on one page and try switiching tabs, its really fast and it resumes from the same state. There is no way you can achieve it by changing fragments on the same container, these are multiple fragments viewed using a viewpager

    – Suhaib Roomy
    May 29 '18 at 9:28





    Its definitely a viewpager. Just scroll on one page and try switiching tabs, its really fast and it resumes from the same state. There is no way you can achieve it by changing fragments on the same container, these are multiple fragments viewed using a viewpager

    – Suhaib Roomy
    May 29 '18 at 9:28













    My guess is also that YouTube or Instagram do not use ViewPager. The restoring happens because of the backStack pop action that resumes the underlying fragment that is added in the first place not replaced

    – HawkPriest
    Sep 6 '18 at 14:13





    My guess is also that YouTube or Instagram do not use ViewPager. The restoring happens because of the backStack pop action that resumes the underlying fragment that is added in the first place not replaced

    – HawkPriest
    Sep 6 '18 at 14:13











    4














    I have made an app like this (still not published on PlayStore) that has the same navigation, maybe its implementation is different from what Google does in their apps, but the functionality is the same.



    the structure involves I have Main Activity that I switch the content of it by showing/hiding fragments using:



    public void switchTo(final Fragment fragment, final String tag /*Each fragment should have a different Tag*/) {

    // We compare if the current stack is the current fragment we try to show
    if (fragment == getSupportFragmentManager().getPrimaryNavigationFragment()) {
    return;
    }

    // We need to hide the current showing fragment (primary fragment)
    final Fragment currentShowingFragment = getSupportFragmentManager().getPrimaryNavigationFragment();

    final FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    if (currentShowingFragment != null) {
    fragmentTransaction.hide(currentShowingFragment);
    }

    // We try to find that fragment if it was already added before
    final Fragment alreadyAddedFragment = getSupportFragmentManager().findFragmentByTag(tag);
    if (alreadyAddedFragment != null) {
    // Since its already added before we just set it as primary navigation and show it again
    fragmentTransaction.setPrimaryNavigationFragment(alreadyAddedFragment);
    fragmentTransaction.show(alreadyAddedFragment);
    } else {
    // We add the new fragment and then show it
    fragmentTransaction.add(containerId, fragment, tag);
    fragmentTransaction.show(fragment);
    // We set it as the primary navigation to support back stack and back navigation
    fragmentTransaction.setPrimaryNavigationFragment(fragment);
    }

    fragmentTransaction.commit();
    }





    share|improve this answer




























      4














      I have made an app like this (still not published on PlayStore) that has the same navigation, maybe its implementation is different from what Google does in their apps, but the functionality is the same.



      the structure involves I have Main Activity that I switch the content of it by showing/hiding fragments using:



      public void switchTo(final Fragment fragment, final String tag /*Each fragment should have a different Tag*/) {

      // We compare if the current stack is the current fragment we try to show
      if (fragment == getSupportFragmentManager().getPrimaryNavigationFragment()) {
      return;
      }

      // We need to hide the current showing fragment (primary fragment)
      final Fragment currentShowingFragment = getSupportFragmentManager().getPrimaryNavigationFragment();

      final FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
      if (currentShowingFragment != null) {
      fragmentTransaction.hide(currentShowingFragment);
      }

      // We try to find that fragment if it was already added before
      final Fragment alreadyAddedFragment = getSupportFragmentManager().findFragmentByTag(tag);
      if (alreadyAddedFragment != null) {
      // Since its already added before we just set it as primary navigation and show it again
      fragmentTransaction.setPrimaryNavigationFragment(alreadyAddedFragment);
      fragmentTransaction.show(alreadyAddedFragment);
      } else {
      // We add the new fragment and then show it
      fragmentTransaction.add(containerId, fragment, tag);
      fragmentTransaction.show(fragment);
      // We set it as the primary navigation to support back stack and back navigation
      fragmentTransaction.setPrimaryNavigationFragment(fragment);
      }

      fragmentTransaction.commit();
      }





      share|improve this answer


























        4












        4








        4







        I have made an app like this (still not published on PlayStore) that has the same navigation, maybe its implementation is different from what Google does in their apps, but the functionality is the same.



        the structure involves I have Main Activity that I switch the content of it by showing/hiding fragments using:



        public void switchTo(final Fragment fragment, final String tag /*Each fragment should have a different Tag*/) {

        // We compare if the current stack is the current fragment we try to show
        if (fragment == getSupportFragmentManager().getPrimaryNavigationFragment()) {
        return;
        }

        // We need to hide the current showing fragment (primary fragment)
        final Fragment currentShowingFragment = getSupportFragmentManager().getPrimaryNavigationFragment();

        final FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        if (currentShowingFragment != null) {
        fragmentTransaction.hide(currentShowingFragment);
        }

        // We try to find that fragment if it was already added before
        final Fragment alreadyAddedFragment = getSupportFragmentManager().findFragmentByTag(tag);
        if (alreadyAddedFragment != null) {
        // Since its already added before we just set it as primary navigation and show it again
        fragmentTransaction.setPrimaryNavigationFragment(alreadyAddedFragment);
        fragmentTransaction.show(alreadyAddedFragment);
        } else {
        // We add the new fragment and then show it
        fragmentTransaction.add(containerId, fragment, tag);
        fragmentTransaction.show(fragment);
        // We set it as the primary navigation to support back stack and back navigation
        fragmentTransaction.setPrimaryNavigationFragment(fragment);
        }

        fragmentTransaction.commit();
        }





        share|improve this answer













        I have made an app like this (still not published on PlayStore) that has the same navigation, maybe its implementation is different from what Google does in their apps, but the functionality is the same.



        the structure involves I have Main Activity that I switch the content of it by showing/hiding fragments using:



        public void switchTo(final Fragment fragment, final String tag /*Each fragment should have a different Tag*/) {

        // We compare if the current stack is the current fragment we try to show
        if (fragment == getSupportFragmentManager().getPrimaryNavigationFragment()) {
        return;
        }

        // We need to hide the current showing fragment (primary fragment)
        final Fragment currentShowingFragment = getSupportFragmentManager().getPrimaryNavigationFragment();

        final FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        if (currentShowingFragment != null) {
        fragmentTransaction.hide(currentShowingFragment);
        }

        // We try to find that fragment if it was already added before
        final Fragment alreadyAddedFragment = getSupportFragmentManager().findFragmentByTag(tag);
        if (alreadyAddedFragment != null) {
        // Since its already added before we just set it as primary navigation and show it again
        fragmentTransaction.setPrimaryNavigationFragment(alreadyAddedFragment);
        fragmentTransaction.show(alreadyAddedFragment);
        } else {
        // We add the new fragment and then show it
        fragmentTransaction.add(containerId, fragment, tag);
        fragmentTransaction.show(fragment);
        // We set it as the primary navigation to support back stack and back navigation
        fragmentTransaction.setPrimaryNavigationFragment(fragment);
        }

        fragmentTransaction.commit();
        }






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jun 19 '18 at 12:48









        Mohammad ErsanMohammad Ersan

        10.3k73866




        10.3k73866























            2














            If you have a bottomNavigationView with 3 items corresponding to 3 Fragments: FragmentA, FragmentB and FragmentC where FragmentA is the startDestination in your navigation graph, then when you're on FragmentB or FragmentC and you click back, you're going to be redirected to FragmentA, that's the behavior recommended by Google and that's implemented by default.



            If however you wish to alter this behavior, you'll need to either use a ViewPager as was suggested by some of the other answers, or manually handle the fragments backStack and back transactions yourself -which in a way would undermine the use of the Navigation component altogether-.






            share|improve this answer



















            • 3





              But youtube, Instagram, Saavn has different behaviour

              – Bincy Baby
              Aug 29 '18 at 2:05











            • True, there's no right or wrong way of doing it, it's just about what google supports by default (and thus recommends) and what your needs are. If these two don't align you need to work around it.

              – Husayn Hakeem
              Aug 30 '18 at 18:26











            • But the problem is that if you are using the JetPack Navigation the backStack will be empty. Apparently JetPack is not adding nothing to the back stack when handling BottomNavigation clicks.

              – Allan Veloso
              Jan 30 at 23:12
















            2














            If you have a bottomNavigationView with 3 items corresponding to 3 Fragments: FragmentA, FragmentB and FragmentC where FragmentA is the startDestination in your navigation graph, then when you're on FragmentB or FragmentC and you click back, you're going to be redirected to FragmentA, that's the behavior recommended by Google and that's implemented by default.



            If however you wish to alter this behavior, you'll need to either use a ViewPager as was suggested by some of the other answers, or manually handle the fragments backStack and back transactions yourself -which in a way would undermine the use of the Navigation component altogether-.






            share|improve this answer



















            • 3





              But youtube, Instagram, Saavn has different behaviour

              – Bincy Baby
              Aug 29 '18 at 2:05











            • True, there's no right or wrong way of doing it, it's just about what google supports by default (and thus recommends) and what your needs are. If these two don't align you need to work around it.

              – Husayn Hakeem
              Aug 30 '18 at 18:26











            • But the problem is that if you are using the JetPack Navigation the backStack will be empty. Apparently JetPack is not adding nothing to the back stack when handling BottomNavigation clicks.

              – Allan Veloso
              Jan 30 at 23:12














            2












            2








            2







            If you have a bottomNavigationView with 3 items corresponding to 3 Fragments: FragmentA, FragmentB and FragmentC where FragmentA is the startDestination in your navigation graph, then when you're on FragmentB or FragmentC and you click back, you're going to be redirected to FragmentA, that's the behavior recommended by Google and that's implemented by default.



            If however you wish to alter this behavior, you'll need to either use a ViewPager as was suggested by some of the other answers, or manually handle the fragments backStack and back transactions yourself -which in a way would undermine the use of the Navigation component altogether-.






            share|improve this answer













            If you have a bottomNavigationView with 3 items corresponding to 3 Fragments: FragmentA, FragmentB and FragmentC where FragmentA is the startDestination in your navigation graph, then when you're on FragmentB or FragmentC and you click back, you're going to be redirected to FragmentA, that's the behavior recommended by Google and that's implemented by default.



            If however you wish to alter this behavior, you'll need to either use a ViewPager as was suggested by some of the other answers, or manually handle the fragments backStack and back transactions yourself -which in a way would undermine the use of the Navigation component altogether-.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Aug 28 '18 at 18:20









            Husayn HakeemHusayn Hakeem

            6261414




            6261414








            • 3





              But youtube, Instagram, Saavn has different behaviour

              – Bincy Baby
              Aug 29 '18 at 2:05











            • True, there's no right or wrong way of doing it, it's just about what google supports by default (and thus recommends) and what your needs are. If these two don't align you need to work around it.

              – Husayn Hakeem
              Aug 30 '18 at 18:26











            • But the problem is that if you are using the JetPack Navigation the backStack will be empty. Apparently JetPack is not adding nothing to the back stack when handling BottomNavigation clicks.

              – Allan Veloso
              Jan 30 at 23:12














            • 3





              But youtube, Instagram, Saavn has different behaviour

              – Bincy Baby
              Aug 29 '18 at 2:05











            • True, there's no right or wrong way of doing it, it's just about what google supports by default (and thus recommends) and what your needs are. If these two don't align you need to work around it.

              – Husayn Hakeem
              Aug 30 '18 at 18:26











            • But the problem is that if you are using the JetPack Navigation the backStack will be empty. Apparently JetPack is not adding nothing to the back stack when handling BottomNavigation clicks.

              – Allan Veloso
              Jan 30 at 23:12








            3




            3





            But youtube, Instagram, Saavn has different behaviour

            – Bincy Baby
            Aug 29 '18 at 2:05





            But youtube, Instagram, Saavn has different behaviour

            – Bincy Baby
            Aug 29 '18 at 2:05













            True, there's no right or wrong way of doing it, it's just about what google supports by default (and thus recommends) and what your needs are. If these two don't align you need to work around it.

            – Husayn Hakeem
            Aug 30 '18 at 18:26





            True, there's no right or wrong way of doing it, it's just about what google supports by default (and thus recommends) and what your needs are. If these two don't align you need to work around it.

            – Husayn Hakeem
            Aug 30 '18 at 18:26













            But the problem is that if you are using the JetPack Navigation the backStack will be empty. Apparently JetPack is not adding nothing to the back stack when handling BottomNavigation clicks.

            – Allan Veloso
            Jan 30 at 23:12





            But the problem is that if you are using the JetPack Navigation the backStack will be empty. Apparently JetPack is not adding nothing to the back stack when handling BottomNavigation clicks.

            – Allan Veloso
            Jan 30 at 23:12











            0














            First, let me clarify how Youtube and Instagram handles fragment navigation.




            • When the user is on a detail fragment, back or up pop the stack once, with the state properly restaured. A second click on the already selected bottom bar item pop all the stack to the root, refreshing it

            • When the user is on a root fragment, back goes to the last menu selected on the bottom bar, displaying the last detail fragment, with the state properly restaured (JetPack doesn't)

            • When the user is on the start destination fragment, back finishes activity


            None of the other answers above solve all this problems using the jetpack navigation.



            JetPack navigation has no standard way to do this, the way that I found more simple is to dividing the navigation xml graph into one for each bottom navigation item, handling the back stack between the navigation items myself using the activity FragmentManager and use the JetPack NavController to handle the internal navigation between root and detail fragments (its implementation uses the childFragmentManager stack).



            Suppose you have in your navigation folder this 3 xmls:



            res/navigation/
            navigation_feed.xml
            navigation_explore.xml
            navigation_profile.xml


            Have your destinationIds inside the navigation xmls the same of your bottomNavigationBar menu ids. Also, to each xml set the app:startDestination to the fragment that you want as the root of the navigation item.



            Create a class BottomNavController.kt:



            class BottomNavController(
            val context: Context,
            @IdRes val containerId: Int,
            @IdRes val appStartDestinationId: Int
            ) {
            private val navigationBackStack = BackStack.of(appStartDestinationId)
            lateinit var activity: Activity
            lateinit var fragmentManager: FragmentManager
            private var listener: OnNavigationItemChanged? = null
            private var navGraphProvider: NavGraphProvider? = null

            interface OnNavigationItemChanged {
            fun onItemChanged(itemId: Int)
            }

            interface NavGraphProvider {
            @NavigationRes
            fun getNavGraphId(itemId: Int): Int
            }

            init {
            var ctx = context
            while (ctx is ContextWrapper) {
            if (ctx is Activity) {
            activity = ctx
            fragmentManager = (activity as FragmentActivity).supportFragmentManager
            break
            }
            ctx = ctx.baseContext
            }
            }

            fun setOnItemNavigationChanged(listener: (itemId: Int) -> Unit) {
            this.listener = object : OnNavigationItemChanged {
            override fun onItemChanged(itemId: Int) {
            listener.invoke(itemId)
            }
            }
            }

            fun setNavGraphProvider(provider: NavGraphProvider) {
            navGraphProvider = provider
            }

            fun onNavigationItemReselected(item: MenuItem) {
            // If the user press a second time the navigation button, we pop the back stack to the root
            activity.findNavController(containerId).popBackStack(item.itemId, false)
            }

            fun onNavigationItemSelected(itemId: Int = navigationBackStack.last()): Boolean {

            // Replace fragment representing a navigation item
            val fragment = fragmentManager.findFragmentByTag(itemId.toString())
            ?: NavHostFragment.create(navGraphProvider?.getNavGraphId(itemId)
            ?: throw RuntimeException("You need to set up a NavGraphProvider with " +
            "BottomNavController#setNavGraphProvider")
            )
            fragmentManager.beginTransaction()
            .setCustomAnimations(
            R.anim.nav_default_enter_anim,
            R.anim.nav_default_exit_anim,
            R.anim.nav_default_pop_enter_anim,
            R.anim.nav_default_pop_exit_anim
            )
            .replace(containerId, fragment, itemId.toString())
            .addToBackStack(null)
            .commit()

            // Add to back stack
            navigationBackStack.moveLast(itemId)

            listener?.onItemChanged(itemId)

            return true
            }

            fun onBackPressed() {
            val childFragmentManager = fragmentManager.findFragmentById(containerId)!!
            .childFragmentManager
            when {
            // We should always try to go back on the child fragment manager stack before going to
            // the navigation stack. It's important to use the child fragment manager instead of the
            // NavController because if the user change tabs super fast commit of the
            // supportFragmentManager may mess up with the NavController child fragment manager back
            // stack
            childFragmentManager.popBackStackImmediate() -> {
            }
            // Fragment back stack is empty so try to go back on the navigation stack
            navigationBackStack.size > 1 -> {
            // Remove last item from back stack
            navigationBackStack.removeLast()

            // Update the container with new fragment
            onNavigationItemSelected()
            }
            // If the stack has only one and it's not the navigation home we should
            // ensure that the application always leave from startDestination
            navigationBackStack.last() != appStartDestinationId -> {
            navigationBackStack.removeLast()
            navigationBackStack.add(0, appStartDestinationId)
            onNavigationItemSelected()
            }
            // Navigation stack is empty, so finish the activity
            else -> activity.finish()
            }
            }

            private class BackStack : ArrayList<Int>() {
            companion object {
            fun of(vararg elements: Int): BackStack {
            val b = BackStack()
            b.addAll(elements.toTypedArray())
            return b
            }
            }

            fun removeLast() = removeAt(size - 1)
            fun moveLast(item: Int) {
            remove(item)
            add(item)
            }
            }
            }

            // Convenience extension to set up the navigation
            fun BottomNavigationView.setUpNavigation(bottomNavController: BottomNavController, onReselect: ((menuItem: MenuItem) -> Unit)? = null) {
            setOnNavigationItemSelectedListener {
            bottomNavController.onNavigationItemSelected(it.itemId)
            }
            setOnNavigationItemReselectedListener {
            bottomNavController.onNavigationItemReselected(it)
            onReselect?.invoke(it)
            }
            bottomNavController.setOnItemNavigationChanged { itemId ->
            menu.findItem(itemId).isChecked = true
            }
            }


            Do your layout main.xml like this:



            <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">

            <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
            app:layout_constraintTop_toTopOf="parent" />

            <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:menu="@menu/navigation" />

            </androidx.constraintlayout.widget.ConstraintLayout>


            Use on your activity like this:



            class MainActivity : AppCompatActivity(),
            BottomNavController.NavGraphProvider {

            private val navController by lazy(LazyThreadSafetyMode.NONE) {
            Navigation.findNavController(this, R.id.container)
            }

            private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) {
            BottomNavController(this, R.id.container, R.id.navigation_feed)
            }

            override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main)

            bottomNavController.setNavGraphProvider(this)
            bottomNavigationView.setUpNavigation(bottomNavController)
            if (savedInstanceState == null) bottomNavController
            .onNavigationItemSelected()

            // do your things...
            }

            override fun getNavGraphId(itemId: Int) = when (itemId) {
            R.id.navigation_feed -> R.navigation.navigation_feed
            R.id.navigation_explore -> R.navigation.navigation_explore
            R.id.navigation_profile -> R.navigation.navigation_profile
            else -> R.navigation.navigation_feed
            }

            override fun onSupportNavigateUp(): Boolean = navController
            .navigateUp()

            override fun onBackPressed() = bottomNavController.onBackPressed()
            }





            share|improve this answer


























            • this solution looks good but there are some things i noticed : <FrameLayout /> should be a NavHostFragment, every graph got it's own home default so doing this if (savedInstanceState == null) bottomNavController .onNavigationItemSelected() will trigger the fragment two times , it doesn't hold states for the fragments.

              – Sim
              Mar 4 at 6:46











            • The idea of the saved instance state exactly avoided the fragment be created two times. I can't check this because I ended up extending the NavController, and creating a custom Navigator exclusively for NavHostFragments, adding it to this NavController (I called NavHostFragmentNavController). Then I create a graph called navigation_main.xml with the <nav-fragment> elements that represent each a bottom navigation item. The new implementations are bigger but the usage quite simple. The code still has some small bugs that I did not finish yet. I will post it when I fix them.

              – Allan Veloso
              Mar 4 at 14:52











            • yes i guess for now extending the NavController is a sane solution, till Google release the sample.

              – Sim
              Mar 5 at 7:18











            • Shouldn't here a destination be passed as the last parameter instead of R.id.navigation_feed? private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) { BottomNavController(this, R.id.container, R.id.navigation_feed) }

              – WWJD
              Mar 5 at 10:19






            • 1





              @WWJD, R.id.navigation_feed is a destination. I named the graph id with the same name as it's an initial destination, so R.navigation.navigation_feed has a R.id.navigation_feed destination.

              – Allan Veloso
              Mar 5 at 14:09
















            0














            First, let me clarify how Youtube and Instagram handles fragment navigation.




            • When the user is on a detail fragment, back or up pop the stack once, with the state properly restaured. A second click on the already selected bottom bar item pop all the stack to the root, refreshing it

            • When the user is on a root fragment, back goes to the last menu selected on the bottom bar, displaying the last detail fragment, with the state properly restaured (JetPack doesn't)

            • When the user is on the start destination fragment, back finishes activity


            None of the other answers above solve all this problems using the jetpack navigation.



            JetPack navigation has no standard way to do this, the way that I found more simple is to dividing the navigation xml graph into one for each bottom navigation item, handling the back stack between the navigation items myself using the activity FragmentManager and use the JetPack NavController to handle the internal navigation between root and detail fragments (its implementation uses the childFragmentManager stack).



            Suppose you have in your navigation folder this 3 xmls:



            res/navigation/
            navigation_feed.xml
            navigation_explore.xml
            navigation_profile.xml


            Have your destinationIds inside the navigation xmls the same of your bottomNavigationBar menu ids. Also, to each xml set the app:startDestination to the fragment that you want as the root of the navigation item.



            Create a class BottomNavController.kt:



            class BottomNavController(
            val context: Context,
            @IdRes val containerId: Int,
            @IdRes val appStartDestinationId: Int
            ) {
            private val navigationBackStack = BackStack.of(appStartDestinationId)
            lateinit var activity: Activity
            lateinit var fragmentManager: FragmentManager
            private var listener: OnNavigationItemChanged? = null
            private var navGraphProvider: NavGraphProvider? = null

            interface OnNavigationItemChanged {
            fun onItemChanged(itemId: Int)
            }

            interface NavGraphProvider {
            @NavigationRes
            fun getNavGraphId(itemId: Int): Int
            }

            init {
            var ctx = context
            while (ctx is ContextWrapper) {
            if (ctx is Activity) {
            activity = ctx
            fragmentManager = (activity as FragmentActivity).supportFragmentManager
            break
            }
            ctx = ctx.baseContext
            }
            }

            fun setOnItemNavigationChanged(listener: (itemId: Int) -> Unit) {
            this.listener = object : OnNavigationItemChanged {
            override fun onItemChanged(itemId: Int) {
            listener.invoke(itemId)
            }
            }
            }

            fun setNavGraphProvider(provider: NavGraphProvider) {
            navGraphProvider = provider
            }

            fun onNavigationItemReselected(item: MenuItem) {
            // If the user press a second time the navigation button, we pop the back stack to the root
            activity.findNavController(containerId).popBackStack(item.itemId, false)
            }

            fun onNavigationItemSelected(itemId: Int = navigationBackStack.last()): Boolean {

            // Replace fragment representing a navigation item
            val fragment = fragmentManager.findFragmentByTag(itemId.toString())
            ?: NavHostFragment.create(navGraphProvider?.getNavGraphId(itemId)
            ?: throw RuntimeException("You need to set up a NavGraphProvider with " +
            "BottomNavController#setNavGraphProvider")
            )
            fragmentManager.beginTransaction()
            .setCustomAnimations(
            R.anim.nav_default_enter_anim,
            R.anim.nav_default_exit_anim,
            R.anim.nav_default_pop_enter_anim,
            R.anim.nav_default_pop_exit_anim
            )
            .replace(containerId, fragment, itemId.toString())
            .addToBackStack(null)
            .commit()

            // Add to back stack
            navigationBackStack.moveLast(itemId)

            listener?.onItemChanged(itemId)

            return true
            }

            fun onBackPressed() {
            val childFragmentManager = fragmentManager.findFragmentById(containerId)!!
            .childFragmentManager
            when {
            // We should always try to go back on the child fragment manager stack before going to
            // the navigation stack. It's important to use the child fragment manager instead of the
            // NavController because if the user change tabs super fast commit of the
            // supportFragmentManager may mess up with the NavController child fragment manager back
            // stack
            childFragmentManager.popBackStackImmediate() -> {
            }
            // Fragment back stack is empty so try to go back on the navigation stack
            navigationBackStack.size > 1 -> {
            // Remove last item from back stack
            navigationBackStack.removeLast()

            // Update the container with new fragment
            onNavigationItemSelected()
            }
            // If the stack has only one and it's not the navigation home we should
            // ensure that the application always leave from startDestination
            navigationBackStack.last() != appStartDestinationId -> {
            navigationBackStack.removeLast()
            navigationBackStack.add(0, appStartDestinationId)
            onNavigationItemSelected()
            }
            // Navigation stack is empty, so finish the activity
            else -> activity.finish()
            }
            }

            private class BackStack : ArrayList<Int>() {
            companion object {
            fun of(vararg elements: Int): BackStack {
            val b = BackStack()
            b.addAll(elements.toTypedArray())
            return b
            }
            }

            fun removeLast() = removeAt(size - 1)
            fun moveLast(item: Int) {
            remove(item)
            add(item)
            }
            }
            }

            // Convenience extension to set up the navigation
            fun BottomNavigationView.setUpNavigation(bottomNavController: BottomNavController, onReselect: ((menuItem: MenuItem) -> Unit)? = null) {
            setOnNavigationItemSelectedListener {
            bottomNavController.onNavigationItemSelected(it.itemId)
            }
            setOnNavigationItemReselectedListener {
            bottomNavController.onNavigationItemReselected(it)
            onReselect?.invoke(it)
            }
            bottomNavController.setOnItemNavigationChanged { itemId ->
            menu.findItem(itemId).isChecked = true
            }
            }


            Do your layout main.xml like this:



            <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">

            <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
            app:layout_constraintTop_toTopOf="parent" />

            <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:menu="@menu/navigation" />

            </androidx.constraintlayout.widget.ConstraintLayout>


            Use on your activity like this:



            class MainActivity : AppCompatActivity(),
            BottomNavController.NavGraphProvider {

            private val navController by lazy(LazyThreadSafetyMode.NONE) {
            Navigation.findNavController(this, R.id.container)
            }

            private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) {
            BottomNavController(this, R.id.container, R.id.navigation_feed)
            }

            override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main)

            bottomNavController.setNavGraphProvider(this)
            bottomNavigationView.setUpNavigation(bottomNavController)
            if (savedInstanceState == null) bottomNavController
            .onNavigationItemSelected()

            // do your things...
            }

            override fun getNavGraphId(itemId: Int) = when (itemId) {
            R.id.navigation_feed -> R.navigation.navigation_feed
            R.id.navigation_explore -> R.navigation.navigation_explore
            R.id.navigation_profile -> R.navigation.navigation_profile
            else -> R.navigation.navigation_feed
            }

            override fun onSupportNavigateUp(): Boolean = navController
            .navigateUp()

            override fun onBackPressed() = bottomNavController.onBackPressed()
            }





            share|improve this answer


























            • this solution looks good but there are some things i noticed : <FrameLayout /> should be a NavHostFragment, every graph got it's own home default so doing this if (savedInstanceState == null) bottomNavController .onNavigationItemSelected() will trigger the fragment two times , it doesn't hold states for the fragments.

              – Sim
              Mar 4 at 6:46











            • The idea of the saved instance state exactly avoided the fragment be created two times. I can't check this because I ended up extending the NavController, and creating a custom Navigator exclusively for NavHostFragments, adding it to this NavController (I called NavHostFragmentNavController). Then I create a graph called navigation_main.xml with the <nav-fragment> elements that represent each a bottom navigation item. The new implementations are bigger but the usage quite simple. The code still has some small bugs that I did not finish yet. I will post it when I fix them.

              – Allan Veloso
              Mar 4 at 14:52











            • yes i guess for now extending the NavController is a sane solution, till Google release the sample.

              – Sim
              Mar 5 at 7:18











            • Shouldn't here a destination be passed as the last parameter instead of R.id.navigation_feed? private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) { BottomNavController(this, R.id.container, R.id.navigation_feed) }

              – WWJD
              Mar 5 at 10:19






            • 1





              @WWJD, R.id.navigation_feed is a destination. I named the graph id with the same name as it's an initial destination, so R.navigation.navigation_feed has a R.id.navigation_feed destination.

              – Allan Veloso
              Mar 5 at 14:09














            0












            0








            0







            First, let me clarify how Youtube and Instagram handles fragment navigation.




            • When the user is on a detail fragment, back or up pop the stack once, with the state properly restaured. A second click on the already selected bottom bar item pop all the stack to the root, refreshing it

            • When the user is on a root fragment, back goes to the last menu selected on the bottom bar, displaying the last detail fragment, with the state properly restaured (JetPack doesn't)

            • When the user is on the start destination fragment, back finishes activity


            None of the other answers above solve all this problems using the jetpack navigation.



            JetPack navigation has no standard way to do this, the way that I found more simple is to dividing the navigation xml graph into one for each bottom navigation item, handling the back stack between the navigation items myself using the activity FragmentManager and use the JetPack NavController to handle the internal navigation between root and detail fragments (its implementation uses the childFragmentManager stack).



            Suppose you have in your navigation folder this 3 xmls:



            res/navigation/
            navigation_feed.xml
            navigation_explore.xml
            navigation_profile.xml


            Have your destinationIds inside the navigation xmls the same of your bottomNavigationBar menu ids. Also, to each xml set the app:startDestination to the fragment that you want as the root of the navigation item.



            Create a class BottomNavController.kt:



            class BottomNavController(
            val context: Context,
            @IdRes val containerId: Int,
            @IdRes val appStartDestinationId: Int
            ) {
            private val navigationBackStack = BackStack.of(appStartDestinationId)
            lateinit var activity: Activity
            lateinit var fragmentManager: FragmentManager
            private var listener: OnNavigationItemChanged? = null
            private var navGraphProvider: NavGraphProvider? = null

            interface OnNavigationItemChanged {
            fun onItemChanged(itemId: Int)
            }

            interface NavGraphProvider {
            @NavigationRes
            fun getNavGraphId(itemId: Int): Int
            }

            init {
            var ctx = context
            while (ctx is ContextWrapper) {
            if (ctx is Activity) {
            activity = ctx
            fragmentManager = (activity as FragmentActivity).supportFragmentManager
            break
            }
            ctx = ctx.baseContext
            }
            }

            fun setOnItemNavigationChanged(listener: (itemId: Int) -> Unit) {
            this.listener = object : OnNavigationItemChanged {
            override fun onItemChanged(itemId: Int) {
            listener.invoke(itemId)
            }
            }
            }

            fun setNavGraphProvider(provider: NavGraphProvider) {
            navGraphProvider = provider
            }

            fun onNavigationItemReselected(item: MenuItem) {
            // If the user press a second time the navigation button, we pop the back stack to the root
            activity.findNavController(containerId).popBackStack(item.itemId, false)
            }

            fun onNavigationItemSelected(itemId: Int = navigationBackStack.last()): Boolean {

            // Replace fragment representing a navigation item
            val fragment = fragmentManager.findFragmentByTag(itemId.toString())
            ?: NavHostFragment.create(navGraphProvider?.getNavGraphId(itemId)
            ?: throw RuntimeException("You need to set up a NavGraphProvider with " +
            "BottomNavController#setNavGraphProvider")
            )
            fragmentManager.beginTransaction()
            .setCustomAnimations(
            R.anim.nav_default_enter_anim,
            R.anim.nav_default_exit_anim,
            R.anim.nav_default_pop_enter_anim,
            R.anim.nav_default_pop_exit_anim
            )
            .replace(containerId, fragment, itemId.toString())
            .addToBackStack(null)
            .commit()

            // Add to back stack
            navigationBackStack.moveLast(itemId)

            listener?.onItemChanged(itemId)

            return true
            }

            fun onBackPressed() {
            val childFragmentManager = fragmentManager.findFragmentById(containerId)!!
            .childFragmentManager
            when {
            // We should always try to go back on the child fragment manager stack before going to
            // the navigation stack. It's important to use the child fragment manager instead of the
            // NavController because if the user change tabs super fast commit of the
            // supportFragmentManager may mess up with the NavController child fragment manager back
            // stack
            childFragmentManager.popBackStackImmediate() -> {
            }
            // Fragment back stack is empty so try to go back on the navigation stack
            navigationBackStack.size > 1 -> {
            // Remove last item from back stack
            navigationBackStack.removeLast()

            // Update the container with new fragment
            onNavigationItemSelected()
            }
            // If the stack has only one and it's not the navigation home we should
            // ensure that the application always leave from startDestination
            navigationBackStack.last() != appStartDestinationId -> {
            navigationBackStack.removeLast()
            navigationBackStack.add(0, appStartDestinationId)
            onNavigationItemSelected()
            }
            // Navigation stack is empty, so finish the activity
            else -> activity.finish()
            }
            }

            private class BackStack : ArrayList<Int>() {
            companion object {
            fun of(vararg elements: Int): BackStack {
            val b = BackStack()
            b.addAll(elements.toTypedArray())
            return b
            }
            }

            fun removeLast() = removeAt(size - 1)
            fun moveLast(item: Int) {
            remove(item)
            add(item)
            }
            }
            }

            // Convenience extension to set up the navigation
            fun BottomNavigationView.setUpNavigation(bottomNavController: BottomNavController, onReselect: ((menuItem: MenuItem) -> Unit)? = null) {
            setOnNavigationItemSelectedListener {
            bottomNavController.onNavigationItemSelected(it.itemId)
            }
            setOnNavigationItemReselectedListener {
            bottomNavController.onNavigationItemReselected(it)
            onReselect?.invoke(it)
            }
            bottomNavController.setOnItemNavigationChanged { itemId ->
            menu.findItem(itemId).isChecked = true
            }
            }


            Do your layout main.xml like this:



            <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">

            <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
            app:layout_constraintTop_toTopOf="parent" />

            <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:menu="@menu/navigation" />

            </androidx.constraintlayout.widget.ConstraintLayout>


            Use on your activity like this:



            class MainActivity : AppCompatActivity(),
            BottomNavController.NavGraphProvider {

            private val navController by lazy(LazyThreadSafetyMode.NONE) {
            Navigation.findNavController(this, R.id.container)
            }

            private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) {
            BottomNavController(this, R.id.container, R.id.navigation_feed)
            }

            override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main)

            bottomNavController.setNavGraphProvider(this)
            bottomNavigationView.setUpNavigation(bottomNavController)
            if (savedInstanceState == null) bottomNavController
            .onNavigationItemSelected()

            // do your things...
            }

            override fun getNavGraphId(itemId: Int) = when (itemId) {
            R.id.navigation_feed -> R.navigation.navigation_feed
            R.id.navigation_explore -> R.navigation.navigation_explore
            R.id.navigation_profile -> R.navigation.navigation_profile
            else -> R.navigation.navigation_feed
            }

            override fun onSupportNavigateUp(): Boolean = navController
            .navigateUp()

            override fun onBackPressed() = bottomNavController.onBackPressed()
            }





            share|improve this answer















            First, let me clarify how Youtube and Instagram handles fragment navigation.




            • When the user is on a detail fragment, back or up pop the stack once, with the state properly restaured. A second click on the already selected bottom bar item pop all the stack to the root, refreshing it

            • When the user is on a root fragment, back goes to the last menu selected on the bottom bar, displaying the last detail fragment, with the state properly restaured (JetPack doesn't)

            • When the user is on the start destination fragment, back finishes activity


            None of the other answers above solve all this problems using the jetpack navigation.



            JetPack navigation has no standard way to do this, the way that I found more simple is to dividing the navigation xml graph into one for each bottom navigation item, handling the back stack between the navigation items myself using the activity FragmentManager and use the JetPack NavController to handle the internal navigation between root and detail fragments (its implementation uses the childFragmentManager stack).



            Suppose you have in your navigation folder this 3 xmls:



            res/navigation/
            navigation_feed.xml
            navigation_explore.xml
            navigation_profile.xml


            Have your destinationIds inside the navigation xmls the same of your bottomNavigationBar menu ids. Also, to each xml set the app:startDestination to the fragment that you want as the root of the navigation item.



            Create a class BottomNavController.kt:



            class BottomNavController(
            val context: Context,
            @IdRes val containerId: Int,
            @IdRes val appStartDestinationId: Int
            ) {
            private val navigationBackStack = BackStack.of(appStartDestinationId)
            lateinit var activity: Activity
            lateinit var fragmentManager: FragmentManager
            private var listener: OnNavigationItemChanged? = null
            private var navGraphProvider: NavGraphProvider? = null

            interface OnNavigationItemChanged {
            fun onItemChanged(itemId: Int)
            }

            interface NavGraphProvider {
            @NavigationRes
            fun getNavGraphId(itemId: Int): Int
            }

            init {
            var ctx = context
            while (ctx is ContextWrapper) {
            if (ctx is Activity) {
            activity = ctx
            fragmentManager = (activity as FragmentActivity).supportFragmentManager
            break
            }
            ctx = ctx.baseContext
            }
            }

            fun setOnItemNavigationChanged(listener: (itemId: Int) -> Unit) {
            this.listener = object : OnNavigationItemChanged {
            override fun onItemChanged(itemId: Int) {
            listener.invoke(itemId)
            }
            }
            }

            fun setNavGraphProvider(provider: NavGraphProvider) {
            navGraphProvider = provider
            }

            fun onNavigationItemReselected(item: MenuItem) {
            // If the user press a second time the navigation button, we pop the back stack to the root
            activity.findNavController(containerId).popBackStack(item.itemId, false)
            }

            fun onNavigationItemSelected(itemId: Int = navigationBackStack.last()): Boolean {

            // Replace fragment representing a navigation item
            val fragment = fragmentManager.findFragmentByTag(itemId.toString())
            ?: NavHostFragment.create(navGraphProvider?.getNavGraphId(itemId)
            ?: throw RuntimeException("You need to set up a NavGraphProvider with " +
            "BottomNavController#setNavGraphProvider")
            )
            fragmentManager.beginTransaction()
            .setCustomAnimations(
            R.anim.nav_default_enter_anim,
            R.anim.nav_default_exit_anim,
            R.anim.nav_default_pop_enter_anim,
            R.anim.nav_default_pop_exit_anim
            )
            .replace(containerId, fragment, itemId.toString())
            .addToBackStack(null)
            .commit()

            // Add to back stack
            navigationBackStack.moveLast(itemId)

            listener?.onItemChanged(itemId)

            return true
            }

            fun onBackPressed() {
            val childFragmentManager = fragmentManager.findFragmentById(containerId)!!
            .childFragmentManager
            when {
            // We should always try to go back on the child fragment manager stack before going to
            // the navigation stack. It's important to use the child fragment manager instead of the
            // NavController because if the user change tabs super fast commit of the
            // supportFragmentManager may mess up with the NavController child fragment manager back
            // stack
            childFragmentManager.popBackStackImmediate() -> {
            }
            // Fragment back stack is empty so try to go back on the navigation stack
            navigationBackStack.size > 1 -> {
            // Remove last item from back stack
            navigationBackStack.removeLast()

            // Update the container with new fragment
            onNavigationItemSelected()
            }
            // If the stack has only one and it's not the navigation home we should
            // ensure that the application always leave from startDestination
            navigationBackStack.last() != appStartDestinationId -> {
            navigationBackStack.removeLast()
            navigationBackStack.add(0, appStartDestinationId)
            onNavigationItemSelected()
            }
            // Navigation stack is empty, so finish the activity
            else -> activity.finish()
            }
            }

            private class BackStack : ArrayList<Int>() {
            companion object {
            fun of(vararg elements: Int): BackStack {
            val b = BackStack()
            b.addAll(elements.toTypedArray())
            return b
            }
            }

            fun removeLast() = removeAt(size - 1)
            fun moveLast(item: Int) {
            remove(item)
            add(item)
            }
            }
            }

            // Convenience extension to set up the navigation
            fun BottomNavigationView.setUpNavigation(bottomNavController: BottomNavController, onReselect: ((menuItem: MenuItem) -> Unit)? = null) {
            setOnNavigationItemSelectedListener {
            bottomNavController.onNavigationItemSelected(it.itemId)
            }
            setOnNavigationItemReselectedListener {
            bottomNavController.onNavigationItemReselected(it)
            onReselect?.invoke(it)
            }
            bottomNavController.setOnItemNavigationChanged { itemId ->
            menu.findItem(itemId).isChecked = true
            }
            }


            Do your layout main.xml like this:



            <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">

            <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
            app:layout_constraintTop_toTopOf="parent" />

            <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:menu="@menu/navigation" />

            </androidx.constraintlayout.widget.ConstraintLayout>


            Use on your activity like this:



            class MainActivity : AppCompatActivity(),
            BottomNavController.NavGraphProvider {

            private val navController by lazy(LazyThreadSafetyMode.NONE) {
            Navigation.findNavController(this, R.id.container)
            }

            private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) {
            BottomNavController(this, R.id.container, R.id.navigation_feed)
            }

            override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main)

            bottomNavController.setNavGraphProvider(this)
            bottomNavigationView.setUpNavigation(bottomNavController)
            if (savedInstanceState == null) bottomNavController
            .onNavigationItemSelected()

            // do your things...
            }

            override fun getNavGraphId(itemId: Int) = when (itemId) {
            R.id.navigation_feed -> R.navigation.navigation_feed
            R.id.navigation_explore -> R.navigation.navigation_explore
            R.id.navigation_profile -> R.navigation.navigation_profile
            else -> R.navigation.navigation_feed
            }

            override fun onSupportNavigateUp(): Boolean = navController
            .navigateUp()

            override fun onBackPressed() = bottomNavController.onBackPressed()
            }






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Feb 15 at 0:17









            aksh1618

            340214




            340214










            answered Feb 3 at 17:23









            Allan VelosoAllan Veloso

            954720




            954720













            • this solution looks good but there are some things i noticed : <FrameLayout /> should be a NavHostFragment, every graph got it's own home default so doing this if (savedInstanceState == null) bottomNavController .onNavigationItemSelected() will trigger the fragment two times , it doesn't hold states for the fragments.

              – Sim
              Mar 4 at 6:46











            • The idea of the saved instance state exactly avoided the fragment be created two times. I can't check this because I ended up extending the NavController, and creating a custom Navigator exclusively for NavHostFragments, adding it to this NavController (I called NavHostFragmentNavController). Then I create a graph called navigation_main.xml with the <nav-fragment> elements that represent each a bottom navigation item. The new implementations are bigger but the usage quite simple. The code still has some small bugs that I did not finish yet. I will post it when I fix them.

              – Allan Veloso
              Mar 4 at 14:52











            • yes i guess for now extending the NavController is a sane solution, till Google release the sample.

              – Sim
              Mar 5 at 7:18











            • Shouldn't here a destination be passed as the last parameter instead of R.id.navigation_feed? private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) { BottomNavController(this, R.id.container, R.id.navigation_feed) }

              – WWJD
              Mar 5 at 10:19






            • 1





              @WWJD, R.id.navigation_feed is a destination. I named the graph id with the same name as it's an initial destination, so R.navigation.navigation_feed has a R.id.navigation_feed destination.

              – Allan Veloso
              Mar 5 at 14:09



















            • this solution looks good but there are some things i noticed : <FrameLayout /> should be a NavHostFragment, every graph got it's own home default so doing this if (savedInstanceState == null) bottomNavController .onNavigationItemSelected() will trigger the fragment two times , it doesn't hold states for the fragments.

              – Sim
              Mar 4 at 6:46











            • The idea of the saved instance state exactly avoided the fragment be created two times. I can't check this because I ended up extending the NavController, and creating a custom Navigator exclusively for NavHostFragments, adding it to this NavController (I called NavHostFragmentNavController). Then I create a graph called navigation_main.xml with the <nav-fragment> elements that represent each a bottom navigation item. The new implementations are bigger but the usage quite simple. The code still has some small bugs that I did not finish yet. I will post it when I fix them.

              – Allan Veloso
              Mar 4 at 14:52











            • yes i guess for now extending the NavController is a sane solution, till Google release the sample.

              – Sim
              Mar 5 at 7:18











            • Shouldn't here a destination be passed as the last parameter instead of R.id.navigation_feed? private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) { BottomNavController(this, R.id.container, R.id.navigation_feed) }

              – WWJD
              Mar 5 at 10:19






            • 1





              @WWJD, R.id.navigation_feed is a destination. I named the graph id with the same name as it's an initial destination, so R.navigation.navigation_feed has a R.id.navigation_feed destination.

              – Allan Veloso
              Mar 5 at 14:09

















            this solution looks good but there are some things i noticed : <FrameLayout /> should be a NavHostFragment, every graph got it's own home default so doing this if (savedInstanceState == null) bottomNavController .onNavigationItemSelected() will trigger the fragment two times , it doesn't hold states for the fragments.

            – Sim
            Mar 4 at 6:46





            this solution looks good but there are some things i noticed : <FrameLayout /> should be a NavHostFragment, every graph got it's own home default so doing this if (savedInstanceState == null) bottomNavController .onNavigationItemSelected() will trigger the fragment two times , it doesn't hold states for the fragments.

            – Sim
            Mar 4 at 6:46













            The idea of the saved instance state exactly avoided the fragment be created two times. I can't check this because I ended up extending the NavController, and creating a custom Navigator exclusively for NavHostFragments, adding it to this NavController (I called NavHostFragmentNavController). Then I create a graph called navigation_main.xml with the <nav-fragment> elements that represent each a bottom navigation item. The new implementations are bigger but the usage quite simple. The code still has some small bugs that I did not finish yet. I will post it when I fix them.

            – Allan Veloso
            Mar 4 at 14:52





            The idea of the saved instance state exactly avoided the fragment be created two times. I can't check this because I ended up extending the NavController, and creating a custom Navigator exclusively for NavHostFragments, adding it to this NavController (I called NavHostFragmentNavController). Then I create a graph called navigation_main.xml with the <nav-fragment> elements that represent each a bottom navigation item. The new implementations are bigger but the usage quite simple. The code still has some small bugs that I did not finish yet. I will post it when I fix them.

            – Allan Veloso
            Mar 4 at 14:52













            yes i guess for now extending the NavController is a sane solution, till Google release the sample.

            – Sim
            Mar 5 at 7:18





            yes i guess for now extending the NavController is a sane solution, till Google release the sample.

            – Sim
            Mar 5 at 7:18













            Shouldn't here a destination be passed as the last parameter instead of R.id.navigation_feed? private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) { BottomNavController(this, R.id.container, R.id.navigation_feed) }

            – WWJD
            Mar 5 at 10:19





            Shouldn't here a destination be passed as the last parameter instead of R.id.navigation_feed? private val bottomNavController by lazy(LazyThreadSafetyMode.NONE) { BottomNavController(this, R.id.container, R.id.navigation_feed) }

            – WWJD
            Mar 5 at 10:19




            1




            1





            @WWJD, R.id.navigation_feed is a destination. I named the graph id with the same name as it's an initial destination, so R.navigation.navigation_feed has a R.id.navigation_feed destination.

            – Allan Veloso
            Mar 5 at 14:09





            @WWJD, R.id.navigation_feed is a destination. I named the graph id with the same name as it's an initial destination, so R.navigation.navigation_feed has a R.id.navigation_feed destination.

            – Allan Veloso
            Mar 5 at 14:09


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f50577356%2fandroid-jetpack-navigation-bottomnavigationview-with-youtube-or-instagram-like%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Mossoró

            Error while reading .h5 file using the rhdf5 package in R

            Pushsharp Apns notification error: 'InvalidToken'