Using androidx navigation framework, I am able to switch between navigation fragment destinations (both using menu and other in-fragment actions). Now, I need to show a share button when I am in a given destination, and hide it in all the others.
I managed to do this, but I need to invoke invalidateOptionsMenu() each time a destination is changed. Is this the right way or are there any more elegant ways?
MainActivity
class MainActivity : AppCompatActivity() {
private var isGivenDestination = false
...
override fun onCreate(savedInstanceState: Bundle?) {
...
navHostFragment.navController.addOnDestinationChangedListener {_, destination, _ ->
invalidateOptionsMenu()
isGivenDestination = destination.id == R.id.given_destination
}
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
val result = super.onPrepareOptionsMenu(menu)
menu?.findItem(R.id.nav_share)?.let {
if (it.isVisible != isGivenDestination) {
it.isVisible = isGivenDestination
}
}
return result
}
}
>Solution :
Your approach to showing and hiding the share button based on the current navigation destination is fundamentally correct. Using invalidateOptionsMenu() in conjunction with onPrepareOptionsMenu() is a standard way to dynamically change menu items.
However, if you’re looking for a more elegant solution, you might consider the following approaches:
- Using LiveData: If you’re already using a
ViewModelwithLiveDatain your app, you can tie the visibility of the share button to aLiveDataobject that represents the current destination.
In your ViewModel:
val isShareVisible: MutableLiveData<Boolean> = MutableLiveData(false)
In your MainActivity:
class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
navHostFragment.navController.addOnDestinationChangedListener { _, destination, _ ->
viewModel.isShareVisible.value = destination.id == R.id.given_destination
}
viewModel.isShareVisible.observe(this, Observer { isVisible ->
invalidateOptionsMenu()
})
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
val result = super.onPrepareOptionsMenu(menu)
menu?.findItem(R.id.nav_share)?.isVisible = viewModel.isShareVisible.value ?: false
return result
}
}
- Directly Modifying the Menu Item
If the change you need to make to the menu is relatively simple and isolated, like just showing or hiding a single item, you can directly modify the visibility of the menu item within theaddOnDestinationChangedListener.
In your MainActivity:
class MainActivity : AppCompatActivity() {
...
private var shareMenuItem: MenuItem? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
navHostFragment.navController.addOnDestinationChangedListener { _, destination, _ ->
shareMenuItem?.isVisible = destination.id == R.id.given_destination
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.your_menu, menu)
shareMenuItem = menu?.findItem(R.id.nav_share)
return true
}
}
Both of these approaches have their own advantages. The LiveData approach integrates well with a ViewModel-based architecture and keeps your UI logic more declaratively structured.
The direct modification approach is simpler and more straightforward but might be less scalable if you have multiple conditions or more complex UI logic to handle.