I\'d like to know how properly handle system back button action using Navigation Controller. In my app I have two fragments (for ex. fragment1 and fragment2) and I have an a
This is 2 lines of code can listen for back press, from fragments, [TESTED and WORKING]
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
//setEnabled(false); // call this to disable listener
//remove(); // call to remove listener
//Toast.makeText(getContext(), "Listing for back press from this fragment", Toast.LENGTH_SHORT).show();
}
Here is my solution
Use androidx.appcompat.app.AppCompatActivity
for the activity that contains the NavHostFragment
fragment.
Define the following interface and implement it in all navigation destination fragments
interface InterceptionInterface {
fun onNavigationUp(): Boolean
fun onBackPressed(): Boolean
}
In your activity override onSupportNavigateUp
and onBackPressed
:
override fun onSupportNavigateUp(): Boolean {
return getCurrentNavDest().onNavigationUp() || navigation_host_fragment.findNavController().navigateUp()
}
override fun onBackPressed() {
if (!getCurrentNavDest().onBackPressed()){
super.onBackPressed()
}
}
private fun getCurrentNavDest(): InterceptionInterface {
val currentFragment = navigation_host_fragment.childFragmentManager.primaryNavigationFragment as InterceptionInterface
return currentFragment
}
This solution has the advantage, that the navigation destination fragments don't need to worry about the unregistering of their listeners as soon as they are detached.
Here is solution that should do what you want, but i think it is a bad solution, because it is going against Android Navigation component idea(letting the android handle the navigation).
Override "onBackPressed" inside your activity
override fun onBackPressed() {
when(NavHostFragment.findNavController(nav_host_fragment).currentDestination.id) {
R.id.fragment2-> {
val dialog=AlertDialog.Builder(this).setMessage("Hello").setPositiveButton("Ok", DialogInterface.OnClickListener { dialogInterface, i ->
finish()
}).show()
}
else -> {
super.onBackPressed()
}
}
}
I tried Jurij Pitulja solution but I just wasn't able to find getOnBackPressedDispatcher or addOnBackPressedCallback also using Kiryl Tkach's solution wasn't able to find the current fragment, so here's mine:
interface OnBackPressedListener {
fun onBackPressed(): Boolean
}
override fun onBackPressed() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
val currentFragment = navHostFragment?.childFragmentManager!!.fragments[0]
if (currentFragment !is OnBackPressedListener || !(currentFragment as OnBackPressedListener).onBackPressed()) super.onBackPressed()
this way you can decide in fragment whether the activity should take control of back pressed or not.
Alternatively, you have BaseActivity for all your activities, you can implement like this
override fun onBackPressed() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
if (navHostFragment != null){
val currentFragment = navHostFragment.childFragmentManager.fragments[0]
if (currentFragment !is AuthContract.OnBackPressedListener ||
!(currentFragment as AuthContract.OnBackPressedListener).onBackPressed()) super.onBackPressed()
} else {
super.onBackPressed()
}
}
you can provide your custom back navigation by using OnBackPressedDispatcher
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// This callback will only be called when MyFragment is at least Started.
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
// Handle the back button event
// and if you want to need navigate up
//NavHostFragment.findNavController(this).navigateUp()
}
// The callback can be enabled or disabled here or in the lambda
}
}
More explanations in android official guide: https://developer.android.com/guide/navigation/navigation-custom-back