Android Navigation Architecture Component - Get current visible fragment
AndroidAndroid NavigationAndroid JetpackAndroid Problem Overview
Before trying the Navigation component I used to manually do fragment transactions and used the fragment tag in order to fetch the current fragment.
val fragment:MyFragment = supportFragmentManager.findFragmentByTag(tag):MyFragment
Now in my main activity layout I have something like:
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/nav_host"
app:navGraph= "@navigation/nav_item"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost= "true"
/>
How can I retrieve the current displayed fragment by the Navigation component? Doing
supportFragmentManager.findFragmentById(R.id.nav_host)
returns a NavHostFragment
and I want to retrieve my shown 'MyFragment`.
Thank you.
Android Solutions
Solution 1 - Android
I managed to discover a way for now and it is as follows:
NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host);
navHostFragment.getChildFragmentManager().getFragments().get(0);
In case of course you know it is the first fragment. I am still investigating a way without this. I agree it is not the best way but that should be something for now.
Solution 2 - Android
There is no way I can find to retrieve the current fragment instance. However, you can get the ID of lastly added fragment using the code below.
navController.currentDestination?.getId()
It returns the ID in navigation file. Hope this helps.
Solution 3 - Android
You should look the childFragmentManager primaryNavigationFragment property of your nav fragment, this is where the current displayed fragment is referenced.
val navHost = supportFragmentManager.findFragmentById(R.id.main_nav_fragment)
navHost?.let { navFragment ->
navFragment.childFragmentManager.primaryNavigationFragment?.let {fragment->
//DO YOUR STUFF
}
}
}
Solution 4 - Android
This seems like the cleanest solution. Update: Changed extension function to property. Thanks Igor Wojda.
1. Create the following extension
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
val FragmentManager.currentNavigationFragment: Fragment?
get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first()
2. In Activity
with NavHostFragment
use it like this:
val currentFragment = supportFragmentManager.currentNavigationFragment
Solution 5 - Android
Use addOnDestinationChangedListener
in activity having NavHostFragment
For Java
NavController navController= Navigation.findNavController(MainActivity.this,R.id.your_nav_host);
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
Log.e(TAG, "onDestinationChanged: "+destination.getLabel());
}
});
For Kotlin
var navController :NavController=Navigation.findNavController(this, R.id.nav_host_fragment)
navController.addOnDestinationChangedListener { controller, destination, arguments ->
Log.e(TAG, "onDestinationChanged: "+destination.label);
}
Solution 6 - Android
this might be late but for anyone who's still looking
val currentFragment = NavHostFragment.findNavController(nav_host_fragment).currentDestination?.id
then you can do something like
if(currentFragment == R.id.myFragment){
//do something
}
note that this is for kotlin, java code should be almost the same
Solution 7 - Android
First, you get the current fragment by id, then you can find the fragment, depending on that id.
int id = navController.getCurrentDestination().getId();
Fragment fragment = getSupportFragmentManager().findFragmentById(id);
Solution 8 - Android
My requirement is to get the current visible fragment from Parent activity,my solution is
private LoginFragment getCurrentVisibleFragment() {
NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().getPrimaryNavigationFragment();
FragmentManager fragmentManager = navHostFragment.getChildFragmentManager();
Fragment loginFragment = fragmentManager.getPrimaryNavigationFragment();
if(loginFragment instanceof LoginFragment){
return (LoginFragment)loginFragment;
}
return null;
}
This may help some one with same problem.
Solution 9 - Android
navController().currentDestination?.id
will give you your current Fragment
's id where
private fun navController() = Navigation.findNavController(this, R.id.navHostFragment)
this id is the id which you have given in your Navigation Graph XML
file under fragment
tag. You could also compare currentDestination.label
if you want.
Solution 10 - Android
Found working solution.
private fun getCurrentFragment(): Fragment? {
val currentNavHost = supportFragmentManager.findFragmentById(R.id.nav_host_id)
val currentFragmentClassName = ((this as NavHost).navController.currentDestination as FragmentNavigator.Destination).className
return currentNavHost?.childFragmentManager?.fragments?.filterNotNull()?.find {
it.javaClass.name == currentFragmentClassName
}
}
Solution 11 - Android
Using navigation components, I got the current displayed fragment using this line of code:
val fragment = parentFragmentManager.primaryNavigationFragment
Solution 12 - Android
From an Activity that uses NavHostFragment
, you can use the code below to retrieve the instance of the Active Fragment
.
val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment
Solution 13 - Android
As per @slhddn answer and comment this is how I'm using it and retrieving the fragment id from the nav_graph.xml file:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(findViewById(R.id.toolbar))
val navController = findNavController(this, R.id.nav_host_fragment)
ivSettingsCog.setOnClickListener {
if (navController.currentDestination?.id == R.id.updatesFragment) // Id as per set up on nav_graph.xml file
{
navController.navigate(R.id.action_updatesFragment_to_settingsFragment)
}
}
}
}
Solution 14 - Android
Within your activity, hosting the NavHostFragment we can write the following piece of code to fetch the instance of the currently displayed fragment
Fragment fragment = myNavHostFragment.getChildFragmentManager().findFragmentById(R.id.navHostFragmentId)
if(fragment instanceOf MyFragment) {
//Write your code here
}
Here R.id.navHostFragmentId is the resId for the NavHostFragment within your activity xml
Solution 15 - Android
Make :
• in some button in your fragment:
val navController = findNavController(this)
navController.popBackStack()
• In your Activity onBackPressed()
override fun onBackPressed() {
val navController = findNavController(R.id.nav_host_fragment)
val id = navController.currentDestination?.getId()
if (id == R.id.detailMovieFragment){
navController.popBackStack()
return
}
super.onBackPressed()
}
Solution 16 - Android
The first fragment in navhost fragment is the current fragment
Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
if (navHostFragment != null)
{Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);}
Solution 17 - Android
The best way I could figure out with 1 fragment, Kotlin, Navigation usage:
On your layout file add the tag: "android:tag="main_fragment_tag""
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/nav_host"
app:navGraph= "@navigation/nav_item"
android:tag="main_fragment_tag"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost= "true"
/>
On your Activity:
val navHostFragment = supportFragmentManager.findFragmentByTag("main_fragment_tag") as NavHostFragment
val mainFragment = navHostFragment.childFragmentManager.fragments[0] as MainFragment
mainFragment.mainFragmentMethod()
Solution 18 - Android
Here is my solution for java
NavHostFragment navHostFragment= (NavHostFragment) getSupportFragmentManager().getFragments().get(0);
MyFragment fragment = (MyFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
fragment.doSomething();
Solution 19 - Android
can you please check with getChildFragmentManager()
call with
NavHostFragment fragment = supportFragmentManager.findFragmentById(R.id.nav_host);
MyFragment frag = (MyFragment) fragment.getChildFragmentManager().findFragmentByTag(tag);
Solution 20 - Android
If you need the instance of your fragment, you can go with:
(Activity) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()
It's very ugly but from there you can check if it's an instance of the fragment you want to play around with
Solution 21 - Android
On Androidx, I have tried many methods for finding out required fragment from NavHostFragment
. Finally I could crack it with below method
public Fragment getFunctionalFragment (String tag_name)
{
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();
Fragment fragment=null;
List fragment_list = navHostManager.getFragments();
for (int i=0; i < fragment_list.size() ; i ++ )
{
if ( fragment_list.get(i) instanceof HomeFragment && tag_name.equals("frg_home")) {
fragment = (HomeFragment) fragment_list.get(i);
break;
}
}
return fragment;
}
Solution 22 - Android
This code works from me
NavDestination current = NavHostFragment.findNavController(getSupportFragmentManager().getPrimaryNavigationFragment().getFragmentManager().getFragments().get(0)).getCurrentDestination();
switch (current.getId()) {
case R.id.navigation_saved:
// WRITE CODE
break;
}
Solution 23 - Android
this might be late but may help someone later.
if you are using nested navigations you can get id like this in Kotlin
val nav = Navigation.findNavController(requireActivity(), R.id.fragment_nav_host).currentDestination
and this is one of fragments in fragment_nav_host.xml
<fragment
android:id="@+id/sampleFragment"
android:name="com.app.sample.SampleFragment"
android:label="SampleFragment"
tools:layout="@layout/fragment_sample" />
and you can check all you need like this
if (nav.id == R.id.sampleFragment || nav.label == "SampleFragment"){}
Solution 24 - Android
Finally the working solution for those of you who are still searching Actually it is very simple and you can achieve this using callback.
follow these steps:
-
Create a listener inside the fragment you want to get the instance from activity onCreate()
public class HomeFragment extends Fragment { private OnCreateFragment createLIstener; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { createLIstener.onFragmentCreated(this); View root = inflater.inflate(R.layout.fragment_home, container, false); //findViews(root); return root; } @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_main, menu); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (item.getItemId()==R.id.action_show_filter){ //do something } return super.onOptionsItemSelected(item); } @Override public void onAttach(Context context) { super.onAttach(context); try { createLIstener = (OnCreateFragment) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener"); } } public interface OnCreateFragment{ void onFragmentCreated(Fragment f); } }
-
Implement your listener in the host Fragment/Activity
public class MainActivity extends AppCompatActivity implements HomeFragment.OnCreateFragment { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BottomNavigationView navView = findViewById(R.id.nav_view); // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( R.id.navigation_home, R. ----) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupWithNavController(navView, navController); } @Override public void onFragmentCreated(Fragment f) { if (f!=null){ H.debug("fragment created listener: "+f.getId()); f.setHasOptionsMenu(true); }else { H.debug("fragment created listener: NULL"); } } }
And that is everything you need to do the job. Hop this help someone
Solution 25 - Android
val fragment: YourFragment? = yourNavHost.findFragment()
Note: findFragment
extension function is located in androidx.fragment.app
package, and it's generic function
Solution 26 - Android
create extension function on Fragment Manager
inline fun <reified T : Fragment> FragmentManager.findNavFragment(hostId: Int): T? {
val navHostFragment = findFragmentById(hostId)
return navHostFragment?.childFragmentManager?.fragments?.findLast {
it is T
} as T?
}
Now simply use it by giving id of the hosting fragment and get the specified fragment
val myFragment: MyFragment? = supportFragmentManager.findNavFragment<MyFragment>(R.id.nav_host_id)
Solution 27 - Android
I combined Andrei Toaders answer and Mukul Bhardwajs answer with an OnBackStackChangedListener
.
As an attribute:
private FooFragment fragment;
In my onCreate
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host);
FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();
FragmentManager.OnBackStackChangedListener listener = () -> {
List<Fragment> frags = navHostManager.getFragments();
if(!frags.isEmpty()) {
fragment = (FooFragment) frags.get(0);
}
};
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
if(destination.getId()==R.id.FooFragment){
navHostManager.addOnBackStackChangedListener(listener);
} else {
navHostManager.removeOnBackStackChangedListener(listener);
fragment = null;
}
});
This way i can get a fragment, which will be displayed later on.
One even may be able to loop though frags
and check multiple fragments with instanceof
.
Solution 28 - Android
I need to do this to setup a bottom navigation view and I did it this way:
val navController = Navigation.findNavController(requireActivity(), R.id.nav_tabs_home)
val bottomNavBar: BottomNavigationView = nav_content_view
NavigationUI.setupWithNavController(bottomNavBar, navController)
Solution 29 - Android
The best way to achieve this is to register a fragment lifecycle callback.
As per the original question, if your NavHostFragment
is defined as...
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/nav_host"
app:navGraph= "@navigation/nav_item"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost= "true"
/>
Then in your main activity onCreate(..)
...
nav_host.childFragmentManager.registerFragmentLifecycleCallbacks(
object:FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentViewCreated(
fm: FragmentManager, f: Fragment, v: View,
savedInstanceState: Bundle?
) {
// callback method always called whenever a
// fragment is added, or, a back-press returns
// to the start-destination
}
}
)
Other callback methods may be overridden to fit your requirements.
Solution 30 - Android
This solution will work in most situations Try this:
val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment?.parentFragment as MainFragment
or this:
val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment as MainFragment
Solution 31 - Android
This works for me
(navController.currentDestination as FragmentNavigator.Destination).className
It will return canonicalName
of your fragment
Solution 32 - Android
In your activity define a fragment object. And from each fragment call this method by passing fragment object, like So the static fragment object will be overridden by current showing fragment every time.
//method in MainActivity
private static Fragment fragment;
public void setFragment(Fragment fragment){
this.fragment = fragment;
}
//And from your fragment class, you can call
((<yourActivity in which fragment present>)getActivity()).setFragment(<your fragment object>);
//if you want to set it directly;
((<yourActivity in which fragment present>)getActivity()).fragment=<your fragment object>;
You can also set callback from fragment to activity, for a better approach and pass your current fragment object.