Alin
Alin

Reputation: 14571

Android Navigation Architecture Component - Get current visible fragment

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.

Upvotes: 173

Views: 147590

Answers (30)

Shubham Goel
Shubham Goel

Reputation: 2176

To communicate between Activity and fragment it is better and easier now to use a shared viewmodel.

just pass activity context while creating the viemmodel instance both in activity and fragment. Look at the code below

In Activity

SharedViewModel sharedViewModel;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class);
    // ...
}

In Fragment

SharedViewModel sharedViewModel;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
    // ...
}

Upvotes: 1

Muhamed El-Banna
Muhamed El-Banna

Reputation: 617

private fun checkIfFragmentVisible(): Boolean {
    findNavController().currentDestination?.let { navDestination ->
        if (navDestination is FragmentNavigator.Destination)
            return navDestination.className == myFragment().javaClass.name

        if (navDestination is DialogFragmentNavigator.Destination)
            return navDestination.className == myFragment().javaClass.name
    }

    return false
}

Upvotes: 0

Nguyen Huy
Nguyen Huy

Reputation: 21

You should try get fragmentHost and then get its first child then instanceof CustomFragment .

Here example

@Override
public void onBackPressed() {
    // get parent host
    Fragment fragmentHost = getSupportFragmentManager().findFragmentById(
            R.id.frame_container);
    // get first child fragment
    Fragment fragment = fragmentHost.getChildFragmentManager()
                                    .getFragments().get(0);
    if (fragment instanceof AccountFragment) {
        Log.d(STRING.TAG, "onBackPressed: home");
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            return;
        }
        
        this.doubleBackToExitPressedOnce = true;
        FancyToast.makeText(this, STRING.message_exit,
                            FancyToast.LENGTH_SHORT, FancyToast.INFO, false)
                  .show();
        
        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
            
            @Override
            public void run() {
                doubleBackToExitPressedOnce = false;
            }
        }, STRING.delayMillis);
    } else {
        Log.d(STRING.TAG, "onBackPressed: not home");
        super.onBackPressed();
    }
}

Upvotes: 1

awariat
awariat

Reputation: 382

I did a bit different in my app than all these answers

  1. I created global variable in class myClass (myClass.kt)

    var currentFragmentName: String = "defaultValue"

  2. When in Fragment I update it (TagsFragment.kt)

    currentFragmentName = "tagsFragment"

  3. And in my case in back button (in parent activity of TagsFragment.kt)

    Log.d(TAG, myClass.currentFragmentName) val homeFragment = HomeFragment() if(InTorry.currentFragmentName=="tagsFragment"){ setCurrentFragment(homeFragment,"fragment_tag_home") }

Upvotes: 0

Pintu Parikh
Pintu Parikh

Reputation: 21

Kotlin version code for get currently active fragment in Navigation Graph

fun getCurrentFragmentInstance(activity: HomeActivity): Fragment? {
    val navHostFragment =
        activity.supportFragmentManager.primaryNavigationFragment as NavHostFragment?
    val fragmentManager: FragmentManager = navHostFragment!!.childFragmentManager
    return fragmentManager.primaryNavigationFragment
}

Upvotes: 0

Shahood ul Hassan
Shahood ul Hassan

Reputation: 789

Add the following method in your Activity class:

private Fragment getCurrentFragment() {
    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
            .findFragmentById(R.id.nav_host);
    if (navHostFragment != null) return navHostFragment.getChildFragmentManager()
            .getPrimaryNavigationFragment();
    return null;
}

That's the shortest and cleanest solution for your problem.

Hope it helps!

Upvotes: 1

CLIFFORD P Y
CLIFFORD P Y

Reputation: 17404

My requirement is to get the current visible fragment from Parent activity,my solution is

 private Fragment getCurrentVisibleFragment() {
        NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().getPrimaryNavigationFragment();
        FragmentManager fragmentManager = navHostFragment.getChildFragmentManager();
        Fragment fragment = fragmentManager.getPrimaryNavigationFragment();
        if(fragment instanceof Fragment ){
            return fragment ;
        }
        return null;
    }

This may help some one with same problem.

Upvotes: 18

Jalees Mukarram
Jalees Mukarram

Reputation: 116

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)

Upvotes: 2

Maximus
Maximus

Reputation: 156

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

Upvotes: 3

Luis Mu&#241;oz
Luis Mu&#241;oz

Reputation: 149

Here is my solution for java

    NavHostFragment  navHostFragment= (NavHostFragment) getSupportFragmentManager().getFragments().get(0);
    MyFragment  fragment = (MyFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
    fragment.doSomething();

Upvotes: 2

Jazib Khan
Jazib Khan

Reputation: 440

Using navigation components, I got the current displayed fragment using this line of code:

val fragment = parentFragmentManager.primaryNavigationFragment

Upvotes: 5

Abu-Bakr
Abu-Bakr

Reputation: 441

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);

Upvotes: 8

Narek Hayrapetyan
Narek Hayrapetyan

Reputation: 1919

val fragment: YourFragment? = yourNavHost.findFragment()

Note: findFragment extension function is located in androidx.fragment.app package, and it's generic function

Upvotes: 1

MoTahir
MoTahir

Reputation: 943

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

Upvotes: 23

patel dhruval
patel dhruval

Reputation: 1052

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()

Upvotes: 2

Ecclesiaste Panda
Ecclesiaste Panda

Reputation: 427

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:

  1. 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);
        }
    }
    
  2. 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

Upvotes: 2

Francislainy Campos
Francislainy Campos

Reputation: 4118

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)
        }

    }

}

}

Upvotes: 5

slhddn
slhddn

Reputation: 1997

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.

Upvotes: 93

Payam Monsef
Payam Monsef

Reputation: 336

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"){}

Upvotes: 1

Eren T&#252;fek&#231;i
Eren T&#252;fek&#231;i

Reputation: 2511

The first fragment in navhost fragment is the current fragment

Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
if (navHostFragment != null) 
{Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);}

Upvotes: 2

Inc
Inc

Reputation: 489

This works for me

(navController.currentDestination as FragmentNavigator.Destination).className 

It will return canonicalName of your fragment

Upvotes: 0

Faisal Shaikh
Faisal Shaikh

Reputation: 4138

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;
}

Upvotes: 1

Aleksandar Ilic
Aleksandar Ilic

Reputation: 1546

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

Upvotes: 36

user2288580
user2288580

Reputation: 2258

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

Upvotes: 0

Mukul Bhardwaj
Mukul Bhardwaj

Reputation: 592

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);

    }

Upvotes: 30

jjz
jjz

Reputation: 2057

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

Upvotes: 3

Abhishek Bansal
Abhishek Bansal

Reputation: 5335

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.

Upvotes: 9

Karthik
Karthik

Reputation: 11

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;
}

Upvotes: 1

Ailelame
Ailelame

Reputation: 11

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

Upvotes: 1

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()
    } 

Upvotes: 2

Related Questions