Charlie Niekirk
Charlie Niekirk

Reputation: 1143

Navigate to fragment on FAB click (Navigation Architecture Components)

I have no idea how to, using the new navigation architecture component, navigate from my main screen (with a FloatingActionButton attatched to a BottomAppBar) to another screen without the app bar.

When I click the fab I want my next screen (fragment?) to slide in from the right. The problem is where do I put my BottomAppBar? If I put it in my MainActivity then I have the issue of the FloatingActionButton not having a NavController set. I also cannot put my BottomAppBar in my Fragment. I am at a loss.

Upvotes: 18

Views: 15304

Answers (5)

bakero98
bakero98

Reputation: 805

This doesn't destroy BottomAppBar. Add this to MainActivity only and don't do anything else

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

fabAdd.setOnClickListener {
   findNavController(navHostFragment).navigate(R.id.fab)
} 

Upvotes: 0

Dariush Malek
Dariush Malek

Reputation: 914

Ran into this issue today and I found out that there is a simple and elegant solution for it.

val navController = findNavController(R.id.navHostFragment)

fabAdd.setOnClickListener {
   navController.navigate(R.id.yourFragment)
}  

This takes care of the navigation. Then you must control the visibility of your BottomAppBar inside your Activity.

Upvotes: 31

Amin Keshavarzian
Amin Keshavarzian

Reputation: 3963

if you wanted to navigate to certain fragment (not the star one) in the beginning for some reason, and also you have to graphs for one activity, here is what I suggest: this method will start activity

companion object {

        const val REQUEST_OR_CONFIRM = "request_or_confirm"
        const val IS_JUST_VIEW = "IS_JUST_VIEW"
        const val MODEL = "model"

        fun open(activity: Activity, isRequestOrConfirm: Boolean, isJustView: Boolean = false, model: DataModel? = null) {
            val intent = Intent(activity, HostActivity::class.java)
            intent.putExtra(REQUEST_OR_CONFIRM, isRequestOrConfirm)
            intent.putExtra(IS_JUST_VIEW, isJustView)
            intent.putExtra(MODEL, model)
            activity.startActivity(intent)
        }
    }

and then in, onCreate method of Host Activity, first decide which graph to use and then pass the intent extras bundle so the start fragment can decide what to do:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_purchase_nav)
        if (intent.getBooleanExtra(REQUEST_OR_CONFIRM, true)) {
            findNavController(R.id.nav_host_fragment).setGraph(R.navigation.nav_first_scenario, intent.extras)
        } else {
            findNavController(R.id.nav_host_fragment).setGraph(R.navigation.nav_second_scenario, intent.extras)
        }
    }

and here's how you can decide what to do in start fragment:

if (arguments != null && arguments!!.getBoolean(HostActivity.IS_JUST_VIEW)){
    navigateToYourDestinationFrag(arguments!!.getParcelable<DataModel>(HostActivity.MODEL))
}

and then navigate like you would do normally:

private fun navigateToYourDestinationFrag(model: DataModel) {
        val action = StartFragmentDirections.actionStartFragmentToOtherFragment(model)
        findNavController().navigate(action)
    }

here's how your graph might look in case you wanted to jump to the third fragment in the beginning enter image description here

PS: make sure you will handle back button on the third fragment, here's a solution

UPDATE: as EpicPandaForce mentioned, you can also start activities using Navigation Components: to do that, first add the Activity to your existing graph, either by the + icon (which didn't work for me) or by manually adding in the xml:

 <activity
        android:id="@+id/secondActivity"
        tools:layout="@layout/activity_second"
        android:name="com.amin.SecondActivity" >
    </activity>

you can also add arguments and use them just like you would in a fragment, with navArgs()

 <activity
        android:id="@+id/secondActivity"
        tools:layout="@layout/activity_second"
        android:name="com.amin.SecondActivity" >
        <argument
            android:name="testArgument"
            app:argType="string"
            android:defaultValue="helloWorld" />
    </activity>

in koltin,here's how you would use the argument, First declare args with the type of generated class named after you activity, in this case SecondActivityArgs in top of your activity class:

val args: SecondActivityArgsby by navArgs()

and then you can use it like this:

print(args.testArgument)

Upvotes: 1

Anmol
Anmol

Reputation: 8670

Put it in MainActivity and setOnClickListener in onStart() of the activity and it will work fine.

override fun onStart() {
  super.onStart()
  floatingActionButton.setOnClickListener {
    it.findNavController().navigate(R.id.yourFragment)
   }
}

Note:This solution is like and hack and better is to follow Activity LifeCycle and setUp OnClickListener when the activity is ready to interact.

Similar question [SOLVED]

Upvotes: 1

Yaswant Narayan
Yaswant Narayan

Reputation: 1342

You could have your BottomAppBar in MainActivity and access your FloatingActionButton in your fragment as follows

activity?.fab?.setOnClickListener { 
    /*...*/
    findNavController().navigate(R.id.action_firstFragment_to_secondFragment, mDataBundle)
}

You could hide the BottomAppBar from another activity as follows

(activity as AppCompatActivity).supportActionBar?.hide()

Make sure you .show() the BottomAppBar while returning to previous fragment

Upvotes: 6

Related Questions