Android onSupportNavigateUp when NavHost is not in MainActivity - android

I have an activity which holds FragmentContainerView. In the FragmentContainerView I have a NavHostFragment. I want to implement onSupportNavigateUp in the NavHostFragment.
class MenuNavHostFragment : Fragment() {
fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController,null)
}
}
in MainActivity.kt
private lateinit var menuNavHostFragment: MenuNavHostFragment
override fun onSupportNavigateUp(): Boolean {
return menuNavHostFragment.onSupportNavigateUp()
}
in fragment_menu_nav_host
...
tools:context=".View.MenuNavHostFragment">
<fragment
android:id="#+id/fragment_menu_nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
...
When I press the back button, my application destroys when I want it to go back one step in the navigation stack.
How do I solve this?

check Update UI components with NavigationUI

If you want go back, use findNavController.navigateUp() function

replace the mentioned code by the following one, according to your program:
open fun navigateUp (drawerLayout: DrawerLayout, navController: NavController):Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navigateUp(this.drawerLayout, navController)
}

Related

lateinit property navController has not been initialized

I was following the official developer.android.com code labs when I encounter this issue
In the MainActivity, you should already have code to set up the app bar (also known as action bar) with the nav controller. Make navController a class variable so you can use it in another method.
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
setupActionBarWithNavController(navController)
}
}
Within the same class, add code to override the onSupportNavigateUp() function. This code will ask the navController to handle navigating up in the app. Otherwise, fall back to back to the superclass implementation (in AppCompatActivity) of handling the Up button.
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
Run the app. The Up button should now work from the FlavorFragment, PickupFragment, and SummaryFragment. As you navigate to previous steps in the order flow, the fragments should show the right flavor and pickup date from the view model.
This is my android studio error

Why not working navigating back when I use optionsMenu to navigate a specific Fragment?

I use Navigatation component latest stable version.
I would like to create a shortcut option menu item in my application, which is navigates to a fragment.
This fragment is also used by the drawer menu.
So when I use the drawer menu for navigation, everything is good, but when I navigate to for example Fragment B and after that I use the options menu to navigate to Fragment C, and after the I can not navigate back to Fragment B from drawer, because it always shows Fragment C.
I can solve this problem by adding navController.popBackStack() before the navigation to Fragment C, but this is not good at all, becaouse the previous fragment is destroyed, and I can not go back to it, by pressing back button.
Is there any solution of this problem?
MainActivity
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main,menu)
menu?.findItem(R.id.action_worksheet_management)?.setOnMenuItemClickListener {
val navController = findNavController(R.id.nav_host_fragment_content_main)
navController.popBackStack()
navController.navigate(R.id.nav_work)
return#setOnMenuItemClickListener true
}
return true
}
private fun setUpNavigation() {
val navController = findNavController(R.id.nav_host_fragment_content_main)
setSupportActionBar(binding.appBarMain.toolbar)
val menuItems = mutableListOf(
R.id.nav_worksheet_browser,
R.id.nav_product,
R.id.nav_about,
R.id.nav_company_info,
R.id.nav_services,
R.id.nav_stock_receive,
R.id.nav_synchronization,
R.id.nav_order_return,
R.id.nav_work,
R.id.nav_settings,
R.id.nav_certificates
)
appBarConfiguration = AppBarConfiguration(
menuItems.toSet(),
binding.drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
binding.navView.setupWithNavController(navController)
}
override fun onBackPressed() {
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_content_main)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
I resolved it, by changed the nav version from 2.4.1 to 2.3.5. I hope it will be fixed later.

Navigate to specific fragment on BottomNavigationView

I've searched my problem but I could not find anything. let me describe my problem.
I have 3 buttons on BottomNavigationView (Home, User, History). then from other activity, I have a button which I want is going to User page (with BottomnavigationView of course.
I will show you my code. it still defaults all. I just do not know how to navigate to the User page.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_user, R.id.navigation_notifications
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}}
and here is my code (from other fragment)
otherFragment.kt
buttonRincian.setOnClickListener {
// i dont know command to go to MainActivity with User page as default selected menu
}
thank you so much, i hoe someone can help me.
Make an interface which your activity will implement.
All the fragments should call the method of the interface wherever you want to change the fragment.
something like
interface TabChangeListener {
fun onTabChange(tabName : String)
}
Code in Activity
class Activity : AppCompatActivity(), TabChangeListener {
override fun onTabChange(tabName : String) {
//code to show different tabs
}
}
Code in Fragments
class Frag1 : Fragment {
var listener : TabChangeListener
//somewhere in the code
listener.onTabChange("userTab")
}
Don't forget to take the reference of TabChangeListener in Fragments from the activity otherwise the callbacks won't work.

Move to MainActivity with Jetpack Navigation

How i can move from SettingsListFragment in my MainActivity?
<fragment
android:id="#+id/settings_list_fragment"
android:name="com.mandarine.targetList.features.settings.SettingsListFragment"
android:label="#string/settings"
tools:layout="#layout/fragment_settings_list">
</fragment>
Because in my navigation graph i haven't screen from MainActivity.
Whole navigation i have in my one activity, u can check code
class MainActivity : AppCompatActivity(), MainActivityViewContract {
private lateinit var auth: FirebaseAuth
private lateinit var mAuthStateListener: FirebaseAuth.AuthStateListener
private val presenter = MainActivityPresenter(contract = this)
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
signIn()
setupViews()
}
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
}
override fun onResume() {
super.onResume()
auth.addAuthStateListener(mAuthStateListener)
}
override fun onPause() {
super.onPause()
auth.removeAuthStateListener(mAuthStateListener)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
presenter.onActivityResult(requestCode, resultCode)
}
override fun cancelSignIn() {
finish()
}
private fun setupViews() {
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
appBarConfiguration = AppBarConfiguration(navController.graph)
val drawerLayout: DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
setOf(R.id.target_list, R.id.settings_list_fragment, R.id.calendar_fragment),
drawerLayout
)
setupActionBar(navController, appBarConfiguration)
setupNavigationMenu(navController)
setupBottomNavMenu(navController)
}
private fun setupNavigationMenu(navController: NavController) {
val sideNavView = findViewById<NavigationView>(R.id.nav_view)
sideNavView?.setupWithNavController(navController)
}
private fun setupActionBar(
navController: NavController,
appBarConfig: AppBarConfiguration
) {
setupActionBarWithNavController(navController, appBarConfig)
}
private fun setupBottomNavMenu(navController: NavController) {
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation_view)
bottomNavigationView?.setupWithNavController(navController)
}
private fun signIn() {
auth = FirebaseAuth.getInstance()
mAuthStateListener = FirebaseAuth.AuthStateListener { firebaseAuth ->
presenter.signIn(activity = this, user = firebaseAuth.currentUser)
}
}
}
You can add an <activity../> tag in your navigation graph and set it as destination in actions.
<activity
android:id="#+id/main_activity_destination"
android:name="com.mandarine.your.package.path.here.MainActivity" />
Then, you can call navController.navigate(R.id.main_activity_destination)
Or, if you want more control over the flow from the nav graph, you can create an action:
<action
android:id="#+id/go_to_main_activity"
app:clearTask="true"
app:destination="#+id/main_activity_destination"
app:launchSingleTop="true"
app:popUpTo="#id/main_activity_destination" />
Then, you can call navController.navigate(R.id.go_to_main_activity)
The question is not completely clear, but it seems like what you are trying to achieve is fragment navigation using NavController. Before answering the question, let me give you a brief understanding of how the new jetpack navigation works.
You will be having a single activity in your application(can have multiple activities as well, but for the sake of explaining, I am sticking with one). In this particular activity, you are having a single NavigationHostFragment, which is responsible for all your app navigation. This NavigationHostFragment will be equivalent to the container for adding Fragments in older way of doing it. The NavigationHostFragment will have a navigation controller, which will be linked to the storyboard. The navigation.xml file which you referred in the question is your storyboard.
The storyboard will contain all the fragments which you need in the navigation and the navigation directions as well. The navigation directions is referred to as actions. Each action in a fragment will be a navigation to a separate fragment.
Hence the navigation has nothing to do with Activity, it is all happening in the NavigationHostFragment(which I hope you already have in place in your Activity's xml file).
With these fundamentals, with two fragments, in which FragmentA is navigating to FragmentB, your navigation xml file will look somewhat like this:
<fragment android:id="#+id/fragmentA" android:name="com.xxx.xxx.FragmentA"
android:label="fragment_a" tools:layout="#layout/fragment_a">
<action android:id="#+id/action_fragmentA_to_fragmentB"
app:destination="#id/fragmentB"/>
</fragment>
<fragment android:id="#+id/fragmentB"
android:name="com.xxx.xxx.FragmentB"
android:label="fragment_b" tools:layout="#layout/fragment_b">
</fragment>
And then in FragmentA code, where you need to perform the navigation, just call:
findNavController().navigate(R.id.action_fragmentA_to_fragmentB)

How to handle up button inside fragment using Navigation Components

I am making a simple note taking app, I have 2 fragments with navigation component, one fragment has a list of notes and the other is for editing or creating a new note.
In MainActivity I added
val navController = this.findNavController(R.id.host_fragment)
NavigationUI.setupActionBarWithNavController(this, navController)
and then override onSupportNavigateUp()
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.host_fragment)
return navController.navigateUp()
}
In NoteEditFragment
requireActivity().onBackPressedDispatcher.addCallback(this) {
saveOrUpdateNote(noteId, note)
}
now it all works well when pressing the "back button" in the device, However onBackPressedDispatcher.addCallback() is note triggered when I press the "up button" the one on the top left of the screen.
My question is : How do I handle this up button from my NoteEditFragment?
Thanks in advance
Finally, I found the solution.
First In the activity onCreate method I had to connect the navigation like I did:
val navController = this.findNavController(R.id.host_fragment)
NavigationUI.setupActionBarWithNavController(this, navController)
Then still in MainActivity override onSupportNavigateUp() :
override fun onSupportNavigateUp(): Boolean
{
val navController = this.findNavController(R.id.host_fragment)
return navController.navigateUp()
}
Then In the Fragment onCreateView I had to enable option menu:
setHasOptionsMenu(true)
then in the fragment I overridden onOptionsItemSelected :
override fun onOptionsItemSelected(item: MenuItem): Boolean
{
// handle the up button here
return NavigationUI.onNavDestinationSelected(item!!,
view!!.findNavController())
|| super.onOptionsItemSelected(item)
}
Note: I think if you have more than one option menu, then I think you have to do a when (item) statement to check what option has been chosen.
Also if you want to handle the device back button then you can do like this in your fragment onCreateViewMethod :
requireActivity().onBackPressedDispatcher.addCallback(this)
{
// handle back button
// change this line to whatever way you chose to navigate back
findNavController().navigate(NoteEditFragmentDirections.actionNoteEditFragmentToNoteListFragment())
}
I think the accepted answer is a bit messy. So, i found a clean code. It is perfect for my use case as i don't need to do anything special when the device back button is pressed. Therefore, anyone here with the same requirements can follow my code.
In onCreate of MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph)
// Check if androidx.navigation.ui.NavigationUI.setupActionBarWithNavController is imported
// By default title in actionbar is used from the fragment label in navigation graph
// To use the app name, remove label else if you want to add customized label specify it there
setupActionBarWithNavController(this, navController, appBarConfiguration)
...
}
Again, in the MainActivity itself:
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.fragment)
// Check if androidx.Navigation.ui navigateUp is imported and used
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
I have added comments for better understanding. If you think the answer is helpful then please upvote. Thank You. Happy Development :)
EDIT 1:
No need to use supportActionBar.setDisplayHomeAsUpEnabled(true) in the fragments. AppBarConfiguration will take care of it.
if you use noActionBar themes and you want to use your own toolbar as an actionBar for the fragment, you can use this method
in the host activity
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.navHostFragment).navigateUp()
}
in the onCreateView of the fragment
(activity as AppCompatActivity).setSupportActionBar(binding.toolbar)
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
in very simple way you just need to set the supportNavigateUp on the Activity.
in MainActivity:
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
setupActionBarWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
in my case its worked

Categories

Resources