2

I have set up my app using the Navigation architecture component. Navigating from one fragment to the next fragment works fine. However I am stuck trying to pass data in that navigation flow.

Following the structure of the Navigation codelab app, I am using the code below to navigate from one fragment to the another on a menu item click:

MainActivity.kt

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return NavigationUI.onNavDestinationSelected(item,
        Navigation.findNavController(this, R.id.nav_host_fragment))
        || super.onOptionsItemSelected(item)
}

I tried passing the bundle here by amending the code as follows. Android Studio does not detect any errors but my app crashes when I try to perform that menu item click. Note, I am passing R.id.menu_edit as an parameter in findNavController because otherwise I get a 'No value passed for parameter 'viewId'' build error - but I think you would normally call this without passing anything.

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item?.itemId) {
        R.id.menu_song_edit -> {
            val songBundle = Bundle()
            songBundle.putString("titleArg", song_title.toString())
            this.findNavController(R.id.menu_song_edit).navigate(R.id.goto_songedit, songBundle) // *** this is line 96 where it crashes per the log ***
            return true
        }
        else -> return NavigationUI.onNavDestinationSelected(item,
                Navigation.findNavController(this, R.id.nav_host_fragment))
                || super.onOptionsItemSelected(item)
    }
}

Log from crash below. NavController was not found?

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.projects.arise.mytestapp, PID: 1162
    java.lang.IllegalStateException: Activity com.projects.arise.mytestapp.MainActivity@58f00cb does not have a NavController set on 2131230836
        at androidx.navigation.Navigation.findNavController(Navigation.java:60)
        at androidx.navigation.ActivityKt.findNavController(Activity.kt:30)
        at com.projects.arise.mytestapp.MainActivity.onOptionsItemSelected(MainActivity.kt:96)
        at android.app.Activity.onMenuItemSelected(Activity.java:3204)
        at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:407)
        at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:195)
        at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
        at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
        at android.support.v7.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:63)
        at android.support.v7.widget.Toolbar$1.onMenuItemClick(Toolbar.java:203)
        at android.support.v7.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:780)
        at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822)
        at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:171)
        at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:973)
        at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:963)
        at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:624)
        at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:150)
        at android.view.View.performClick(View.java:5610)
        at android.view.View$PerformClick.run(View.java:22265)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756) Disconnected from the target VM, address: 'localhost:8610', transport: 'socket'

Then I tried overriding onOptionsItemSelected in the fragment that's passing the data instead but that did not seem to have any effect. I'm guessing that's because it is being intercepted already by the same method in MainActivity.kt above.

The code I had in the data sending fragment was as follows. Resource ID 'goto_detailsedit' refers to the ID of the action in the navigation nav_graph.xml file.

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when (item?.itemId) {
        R.id.menu_edit -> {
            val songBundle = Bundle()
            songBundle.putString("nameArg", first_name.text.toString())
            this.findNavController().navigate(R.id.goto_songedit, songBundle)
            return true
        }
        else -> return super.onOptionsItemSelected(item)
    }
}

Can someone please help me with the rookie syntax error in the MainActivity.kt code above?

UPDATE 2 Jul 2018

Going through the documentation again I found that the syntax should be as follows - I need to reference a view:

view.findNavController().navigate(R.id.confirmationAction, bundle)

I have already set up the toolbar in onCreate in the main activity by calling:

val toolbar = findViewById<Toolbar>(R.id.toolbar)
    setSupportActionBar(toolbar)

So I tried referencing the toolbar by replacing this with toolbar:

toolbar.findNavController().navigate(R.id.goto_songedit, songBundle)

Android Studio code editor is now happy with it but my app still crashes. The log states that the NavController isn't set up yet:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.projects.arise.mytestapp, PID: 2670
    java.lang.IllegalStateException: View android.support.v7.widget.Toolbar{d03bbdd V.E...... ........ 0,0-1080,147 #7f0800e0 app:id/toolbar} does not have a NavController set
        at androidx.navigation.Navigation.findNavController(Navigation.java:83)
        at androidx.navigation.ViewKt.findNavController(View.kt:28)
        at com.projects.arise.mytestapp.MainActivity.onOptionsItemSelected(MainActivity.kt:96)
        ...

Is there something I'm missing? Or could this be a bug with Navigation?

Dennis 815
  • 139
  • 1
  • 2
  • 12
  • Try Navigation.findNavController instead of this.findNavController – Alex Jul 01 '18 at 14:41
  • It seems `Navigation.findNavController' won't take a bundle as I am getting an error. As per the [link](https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing#kotlin) documentation it only takes either a view or activity and resource ID. – Dennis 815 Jul 01 '18 at 14:49
  • Try NavHostFragment.findNavController(nav_host_fragment).navigate(R.id.goto_songedit, songBundle) Anyway using bundle is not safeargs, it is regular parameters passing, if you want to use safeargs you need to declare them inside navigation graph. – Alex Jul 02 '18 at 05:22
  • That works!! Thank you so much!!! Do you want to post it as an answer? I'll then upvote and accept it as the right answer. Yes I realised during the day today that the bundle is not created using safeargs, rather safeargs is used at the receiving end if I'm not mistaken. I was hoping I had the later part all hooked up correctly and it was. It all now works perfectly! But I get what you mean so I'll edit the question. – Dennis 815 Jul 02 '18 at 11:23
  • https://stackoverflow.com/a/51135410/1268507 – Alex Jul 02 '18 at 11:45

2 Answers2

11

You should use 'NavHostFragment.findNavController()':

    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when (item?.itemId) { 
        R.id.menu_edit -> { 
            val songBundle = Bundle() 
            songBundle.putString("nameArg", first_name.text.toString()) 
            NavHostFragment.findNavController(nav_host_fragment).navigate(R.id.goto_songedit, songBundle) 
            return true 
        } 
        else -> return super.onOptionsItemSelected(item) 
    } 
} 
John Joe
  • 12,412
  • 16
  • 70
  • 135
Alex
  • 9,102
  • 3
  • 31
  • 35
1

Perfect Solution:

//-------------------/To send Data/--------------------

   val bundle = Bundle()
     bundle.putString("data","Hello World!")
     Navigation.findNavController(view).navigate(R.id.action_firstFragment_to_secondFragment,bundle)


//--------------------/To get data/----------------------------------

 val string = arguments!!.getString("data")
Toast.makeText(context,string,Toast.LENGTH_SHORT).show()
Vishva Vijay
  • 340
  • 3
  • 11