safe-args to pass Bundles safely between Activities/Fragments

Previously there was no way or guaranteeing that the data handed across in Bundles was what was expected. The safe-args plugin seeks to address this:

dependencies {
  ...
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"

   // NOTE: Do not place your application dependencies here; they belong
   // in the individual module build.gradle files
}
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-kapt'

// Add the safe-args plugin after any others:
apply plugin: 'androidx.navigation.safeargs'

android {
    compileSdkVersion 28
    ...

In the Navigation component select the Fragment which is passing on arguments and add Arguments using the + symbol next to the Arguments section of the GUI. Give each a name and specify type if necessary.

import ...

class TitleFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val binding: FragmentTitleBinding = DataBindingUtil.inflate(
                inflater, R.layout.fragment_title, container, false)
        binding.playButton.setOnClickListener {v: View ->
 //               v.findNavController().navigate(R.id.action_titleFragment_to_nextFragment) // Before safe-args
          v.findNavController().navigate(TitleFragmentDirections.actionTitleFragmentToNextFragment(firstParameter, secondParameter)) // With safe-args. Creates a class *Directions based on Fragment name with associated actions which take the parameters to be passed on
        }
        setHasOptionsMenu(true)
        return binding.root
    }

    override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater?.inflate(R.menu.overflow_menu, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        return NavigationUI.onNavDestinationSelected(item!!, view!!.findNavController())
                || super.onOptionsItemSelected(item)
    }
}

In the receiving fragment:

import ...

class NextFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val binding: FragmentTitleBinding = DataBindingUtil.inflate(
                inflater, R.layout.fragment_next, container, false)
        val args = NextFragmentArgs.fromBundle(arguments!!) // Get arguments.
        Toast.makeText(context, "First Parameter: ${args.firstParameter}, Second Parameter: ${args.secondParameter}", Toast.LENGTH_SHORT).show() // Use the Bundled parameters
        return binding.root
    }
}

 

Add overflow menu with Navigation component in Kotlin

Create a menu resource file in the res/menu folder:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/aboutFragment"
        android:title="@string/about" />

</menu>

In the relevant Activity/Fragment:

import ...

class TitleFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val binding: FragmentTitleBinding = DataBindingUtil.inflate(
                inflater, R.layout.fragment_title, container, false)
        setHasOptionsMenu(true) // State that we want an overflow menu
        return binding.root
    }

    override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { // Override onCreateOptionsMenu
        super.onCreateOptionsMenu(menu, inflater)
        inflater?.inflate(R.menu.overflow_menu, menu) // Feed in the xml resource you created
    }

    override fun onOptionsItemSelected(item: MenuItem?): Boolean { // Override onOptionsItemSelected 
        return NavigationUI.onNavDestinationSelected(item!!, view!!.findNavController()) // Use the NavigationUI class to determine if you clicked an item in your list
                || super.onOptionsItemSelected(item) // If not, defer to super
    }
}

 

Up Navigation support in Navigation component

In Launcher Activity:

import ...

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        @Suppress("UNUSED_VARIABLE")
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val navController = this.findNavController(R.id.myNavHostFragment) // Find the NavController
        NavigationUI.setupActionBarWithNavController(this, navController) // Setup the ActionBar
    }

    override fun onSupportNavigateUp(): Boolean { // Override the Up Navigation
        val navController = this.findNavController(R.id.myNavHostFragment)
        return navController.navigateUp()
    }
}