I'm attempting to pass an object between navigation fragments. I'm able to build the project, but when it launches, I get an error on the nav_graph stating: "Exception inflating nav_graph line 20". Line 20 is the argument line on the nav_graph. I just added the #Parcelize keyword to the top of the class I'm trying to pass and setup the nav_graph. Do I need to do something else?
Team Class:
#Parcelize
public class Team {
#SerializedName("idTeam")
#Expose
private String idTeam;
#SerializedName("idSoccerXML")
#Expose
private String idSoccerXML;
#SerializedName("idAPIfootball")
#Expose
private String idAPIfootball;
#SerializedName("intLoved")
#Expose
private String intLoved;
#SerializedName("strTeam")
#Expose
private String strTeam;
#SerializedName("strTeamShort")
#Expose
private String strTeamShort;
Nav_Graph:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/homeFragment"
android:name="com.jaykallen.searchapi.ui.HomeFragment"
android:label="HomeFragment">
<action
android:id="#+id/action_homeFragment_to_resultsFragment"
app:destination="#id/resultsFragment" />
</fragment>
<fragment
android:id="#+id/resultsFragment"
android:name="com.jaykallen.searchapi.ui.ResultsFragment"
android:label="ResultsFragment">
<argument
android:name="choice"
app:argType="com.jaykallen.searchapi.model.Team"
app:nullable="true" />
</fragment>
</navigation>
HomeFragment Method:
private fun choiceClicked(chosen: Team) {
println("User clicked: ${chosen.strTeam}")
homeViewModel.choice = chosen
val action = HomeFragmentDirections.actionHomeFragmentToResultsFragment(chosen)
Navigation.findNavController(view!!).navigate(action)
}
ResultsFragment Method:
private fun getSafeArgs() {
arguments?.let {
val args = ResultsFragmentArgs.fromBundle(it)
teamChosen = args.choice
if (teamChosen != null) {
println("Safe Argument Received=${teamChosen?.strTeam}")
updateUi(teamChosen)
}
}
}
It turns out, all you needed to do is implement the Parcelable interface on your Java object. Normally, if you were using Kotlin, the #Parcelize annotation wouldn't have allowed you to compile without the Parcelable interface. It seems this compile time protection doesn't work for Java code.
By using Java objects, you'll also lose out on all of the automatic code generation goodies that come with the #Parcelize annotation.
In other words, I recommend you convert your Java file to Kotlin if you would like to take advantage of the #Parcelize annotation.
Related
It is reasonable that a Fragment might be used from several sub-graphs in the navigation hierarchy. In this case if the fragment depends on a view model provided by the parent Fragment the view model needs to be in a sub-graph scope that changes depending on its parent.
Kotlin provides a convenient way to get a graph scoped view model:
private val fvm: SoftenerViewModel by navGraphViewModels(R.id.navigation_graph_softener)
but this hard codes in the sub-graph id.
What is the best way to address this case?
One approach is the following, but given that the new extension function is not already in the library it raises the question if there is a better way?
By direct analogy with the Kotlin supplied extension function define the following extension:
/**
* Derived from [androidx.navigation.navGraphViewModels]
*/
#MainThread
inline fun <reified VM : ViewModel> Fragment.navGraphViewModels(
viewModelArgKey: String,
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
val backStackEntry by lazy {
val id = arguments?.getInt(viewModelArgKey, 0)?:0
require(id != 0) {"Fragment argument $viewModelArgKey required."}
findNavController().getBackStackEntry(id)
}
val storeProducer: () -> ViewModelStore = {
backStackEntry.viewModelStore
}
return createViewModelLazy(VM::class, storeProducer, {
factoryProducer?.invoke() ?: backStackEntry.defaultViewModelProviderFactory
})
}
This differs only in that the argument is a String, and the name of the argument is what needs to be given. The laziness ensures that the fragment arguments are referenced at the right time.
It is used just like the existing androidx library extension:
private val fvm: SoftenerViewModel by navGraphViewModels("view_model_id")
The only requirement is the navigation sub-graph defines the argument:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#+id/navigation_home"
>
<fragment
android:id="#+id/navigation_home"
android:name="com...HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home"
/>
<navigation
android:id="#+id/navigation_graph_softener"
android:label="Softener"
app:startDestination="#id/navigation_softener"
>
<fragment
android:id="#+id/navigation_softener"
android:name="com.hanafey.android.waterstats.ui.softener.SoftenerFragment"
android:label="#string/title_softener"
tools:layout="#layout/fragment_softener"
/>
<fragment
android:id="#+id/navigation_softener_history"
android:name="com...SoftenerHistoryFragment"
android:label="Softener History"
tools:layout="#layout/fragment_softener_history"
>
<argument
android:name="view_model_id"
app:argType="reference"
android:defaultValue="#id/navigation_graph_softener"
/>
</fragment>
</navigation>
</navigation>
I have a single activity which has a bottom navigation view in order to open different fragments.
The top level/default fragments loads data from firebase and then i want to pass that data to different fragments when user switches to different fragment.
Navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/navigation"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/homeFragment"
android:name="com.ankitrath.finderr.HomeFragment"
android:label="fragment_home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/searchFragment"
android:name="com.ankitrath.finderr.SearchFragment"
android:label="fragment_search"
tools:layout="#layout/fragment_search" />
<fragment
android:id="#+id/requestFragment"
android:name="com.ankitrath.finderr.RequestFragment"
android:label="fragment_request"
tools:layout="#layout/fragment_request" />
<fragment
android:id="#+id/friendFragment"
android:name="com.ankitrath.finderr.FriendFragment"
android:label="fragment_friend"
tools:layout="#layout/fragment_friendd" />
</navigation>
My MainActivity's OnCreate has:
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
NavController navController = Navigation.findNavController(this, R.id.fragment);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
When HomeFragment loads the data from firestore. I want to pass 2 values to the other fragments.
I did look up the docs but I couldn't understand.
The easiest solution for this is to just use Viewmodel instead of passing it between activity and the fragment. Then u pass the activity on the ViewModel so every fragment and the parent have the same ViewModel.
This is an example of how to do it val viewModel by activityViewModels<The ViewModel that u made>()
you can use VIEWHOLDER to share data between fragments or activities, here is an example
in your app gradle add this implementation
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
Create a viewHolder Class like that
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class SharedDataViewModel: ViewModel() {
private val _selectedCycle = MutableLiveData<Cycle>()
val selectedCycle: LiveData<Cycle> = _selectedCycle
private val _isAdmin = MutableLiveData<Boolean>()
val isAdmin: LiveData<Boolean> = _isAdmin
fun getSelectedCycle():Cycle {
return _selectedCycle.value!!
}
fun setSelectedCycle(cycle: Cycle) {
_selectedCycle.value = cycle
}
fun getIsAdmin():Boolean {
return _isAdmin.value!!
}
fun setIsAdmin(value: Boolean) {
_isAdmin.value = value
}
}
and use it like that in every fragment or activity
private val sharedData: SharedDataViewModel by activityViewModels()
then set or get the new values
sharedData.getIsAdmin()
sharedData.setIsAdmin(true)
I have implemented an argument to be passed between fragments in nav_graph, however when I attempt to set the argument in the originating fragment, the argument is not found by the NavDirections.
Note that Navigation works fine before trying to pass the argument.
If I do a Clean Project I lose the NavDirections. If I do a Rebuild I lose the argument.
Gradle:app
//Navigation
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
apply plugin: "androidx.navigation.safeargs.kotlin"
nav_graph.xml
<fragment
android:id="#+id/destination_home"
android:name="com.android.joncb.flightlogbook.HomeFragment"
android:label="#string/lblHome"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/action_home_to_fltHistory"
app:destination="#id/destination_fltHistory" />
<action
android:id="#+id/action_home_to_stats"
app:destination="#id/destination_statistics" />
<action
android:id="#+id/action_home_to_newFlight"
app:destination="#id/destination_newFlight" />
<action
android:id="#+id/action_home_to_fltDetails"
app:destination="#id/destination_fltDetails" />
<argument
android:name="fltData"
app:argType="string" />
</fragment>
and in my Home Fragment I get the error "Unresolved reference: fltData"
card_nextFlight.setOnClickListener {
val actionDetails = HomeFragmentDirections.actionHomeToFltDetails()
actionDetails.fltData ( flightData.toString())
Navigation.findNavController(it).navigate(actionDetails)
}
flightData is a data class
data class FlightDTO(
var airlineName: String, var faCode: String, var fltNo: String, var aircraft: String,
var depAP: String, var arrAP: String, var schedDep: String, var schedArr: String,
var date: String, var leg: Int = 0, var actDep: String = "", var actArr: String = "" ){
...
override fun toString(): String {
return "$airlineName $faCode $fltNo $aircraft $depAP $schedDep $arrAP $schedDep $date"
}
}
I want to pass the class ideally by making the class Parcelable, but until I can pass a string, there is no point venturing down the parcel line.
You are writing your XML wrong, think like this : The way I structure my XML properties is the way the generated code will look like and received between destinations sort of...
So basically in your nav_graph.xml you should change to:
<fragment
android:id="#+id/destination_home"
android:name="com.android.joncb.flightlogbook.HomeFragment"
android:label="#string/lblHome"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/action_home_to_fltHistory"
app:destination="#id/destination_fltHistory" />
<action
android:id="#+id/action_home_to_stats"
app:destination="#id/destination_statistics" />
<action
android:id="#+id/action_home_to_newFlight"
app:destination="#id/destination_newFlight" />
<action
android:id="#+id/action_home_to_fltDetails"
app:destination="#id/destination_fltDetails">
<argument
android:name="fltData"
app:argType="string" />
</action>
</fragment>
and in your destination it should look something like:
<fragment
android:id="#+id/destination_fltDetails"
android:name="com.android.joncb.flightlogbook.FlightDetailsFragment"
android:label="#string/lblFlightDetails"
tools:layout="#layout/fragment_flight_details">
<argument
android:name="fltData"
app:argType="string" />
</fragment>
and in your flight details fragment the properties are received by using:
private val args: FlightDetailsFragmentArgs by navArgs()
println(args.fltData) // prints the navigation data
UPDATE:
Forgot to mention your OnClickListener in your Home fragment that would look more like this:
card_nextFlight.setOnClickListener {
val actionDetails = HomeFragmentDirections.actionHomeToFltDetails(flightData.toString())
Navigation.findNavController(it).navigate(actionDetails)
}
For my case, I wrote a buggy code like that -
NavController navController = NavHostFragment.findNavController(this);
NavDirections navDirections = MyDestinationFragmentDirections.actionMyAction(myArgumentValue);
navController.navigate(navDirections.getActionId());
Then I change the last line into this -
navController.navigate(navDirections);
And finally,it worked as expected!!!
The logic behind this was, in NavController class the method which accepting int (resId of action) always put null argument -
public void navigate(#IdRes int resId) {
navigate(resId, null);
}
So we should use -
public void navigate(#NonNull NavDirections directions) {
navigate(directions.getActionId(), directions.getArguments());
}
method if we are willing to pass an arguments via an action.
my mistake was the following. I had something like
NavDirections action =
SpecifyAmountFragmentDirections
.actionSpecifyAmountFragmentToConfirmationFragment();
I changed to something like
ConfirmationAction action =
SpecifyAmountFragmentDirections
.actionSpecifyAmountFragmentToConfirmationFragment();
Rather than pass a data class, I have created a JSON String and passed a string
card_nextFlight.setOnClickListener {
val dataString = flightData.toJSONString()
val actionDetails = HomeFragmentDirections.actionHomeToFltDetails(dataString)
Navigation.findNavController(it).navigate(actionDetails)
}
To get this to work I had to modify the actionHomeToFltDetails function to receive a string in HomeFragmentsDirections
fun actionHomeToFltDetails(fltData: String): NavDirections = ActionHomeToFltDetails(fltData)
}
I could not get #Lucho approach to handle the arg in the destination fragment to work so reverted to bundle management, and converted the JSON string back to a data class
const val ARG_PARAM1 = "fltData"
.
.
.
arguments?.let {
argFltData = it.getString(ARG_PARAM1)
Log.e("args","Passed Argument: $argFltData")
fltData = gson.fromJson(argFltData, FlightDTO::class.java)
}
Thanks again for your input and I hope this helps someone else through the drama.
What I have done:
I have created Navigation Drawer Activity, As updated new format of Navigation Drawer Activity, As per new Android architecture, I got it with Navigation Component structure.
The NavigationView code with NavController and NavigationUI is below which is opening fragment when I click on any navigation item.
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_profile, R.id.nav_privacy_policy,
R.id.nav_terms, R.id.nav_contact_us, R.id.nav_share, R.id.nav_send)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
This is for nav_host_fragment:
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
The navigation is happening using this navigation/mobile_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#+id/nav_home">
<fragment
android:id="#+id/nav_home"
android:name="com.sohamerp.marsremedies.fragment.HomeFragment"
android:label="#string/menu_home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="#string/menu_my_profile"
tools:layout="#layout/fragment_profile" />
<fragment
android:id="#+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="#string/menu_privacy_policy"
tools:layout="#layout/fragment_privacy_policy" />
<fragment
android:id="#+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.TermsConditionFragment"
android:label="#string/menu_terms"
tools:layout="#layout/fragment_terms_condition" />
<fragment
android:id="#+id/nav_contact_us"
android:name="com.sohamerp.marsremedies.fragment.ContactUsFragment"
android:label="#string/menu_contact_us"
tools:layout="#layout/fragment_terms_condition" />
</navigation>
What I want to do:
Now I want to pass some values as a bundle (arguments) in Fragment when it's called.
Scenario: I have two fragments PrivacyPolicyFragment and TermsConditionsFragment, In both fragments, I am just opening links inside WebView accordingly. So When I click on the menu item of Privacy Policy, I will pass a link related to the same.
In this new structure navigation/mobile_navigation.xml opening fragments, How can I pass arguments?
Any help?
So I forgot to go through this link : Define Destination Arguments
But this answer helpful to all lazy peoples like me:
Add dependency in project level build.gradle:
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0"
Apply plugin in app level build.gradle:
apply plugin: "androidx.navigation.safeargs"
Using XML: predefined (static) value:
In xml file of navigation /navigation/mobile_navigation.xml declare argument tag as below or you can design through this link:
<fragment
android:id="#+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="#string/menu_privacy_policy"
tools:layout="#layout/fragment_privacy_policy" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
<fragment
android:id="#+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="#string/menu_terms"
tools:layout="#layout/fragment_terms_condition" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
Now you have to write code in your Fragment like:
if(getArguments() != null) {
// The getPrivacyPolicyLink() method will be created automatically.
String url = PrivacyPolicyFragmentArgs.fromBundle(getArguments()).getPrivacyPolicyLink();
}
Hope it will helps you others.
Simple and fast solution:
pass arguments between destinations
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
and receiving
TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));
In this scenario, you can use
private NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
// Create the Bundle to pass, you can put String, Integer, or serializable object
Bundle bundle = new Bundle();
bundle.putString("link","http://yourlink.com/policy");
bundle.putSerializable("USER", user); // Serializable Object
navController.navigate(R.id.nav_terms, bundle); // called fragment with agruments
In case of any help you can reply on it
To pass arguments to other Fragments/Destinations, use Safe Args which ensures type safety. Just like #bromden illustrated, Safe Args will generate a class for each fragment/destination where an action originates. You can then pass the arguments into the action that navigates to the Fragments.
In the receiving fragment, say PrivacyFragment if your code is in Kotlin, use by navArgs() property delegate to access the arguments. i.e.
val args: PrivacyFragmentArgs by navArgs()
To better understand this, visit Pass data between destinations
In newer version of Android Studio 3.2+, below dependency and plug-in need to add in both build.gradle file
Step-1
Add dependency in Project-Level build.gradle
dependencies {
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5'
}
Apply plugins in App-Level build.gradle
plugins {
id 'androidx.navigation.safeargs'
}
Step-2
In Navigation file, res/navigation/nav_graph.xml
Declare argument tag in any fragment or inner fragment with action tag
List item
Sample xml code
<fragment
android:id="#+id/nav_register"
android:name="com.pd.demo.ui.profile.RegisterFragment"
android:label="#string/title_register"
tools:layout="#layout/fragment_register">
<action
android:id="#+id/action_nav_register_to_nav_verify_otp"
app:destination="#id/nav_verify_otp">
<argument
android:name="mobile"
app:argType="string" />
<argument
android:name="password"
app:argType="string" />
</action>
</fragment>
Step-3
Below Kotlin code, pass argument to destination fragment
val bundle = bundleOf("mobile" to binding.etMobileNo.text.toString().trim())
Navigation.findNavController(binding.root).navigate(R.id.action_nav_register_to_nav_verify_otp, bundle)
Step-4
Below Kotlin code, get bundle argument from source fragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mobileNo = arguments!!.getString("mobile").toString()
password = arguments!!.getString("password").toString()
}
This code will helps
You could implement NavigationView.OnNavigationItemSelectedListener
And do something like this:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
drawer_layout.closeDrawers()
if (item.itemId == nv_navigation_drawer_navigation_view.checkedItem?.itemId)
return false
Handler().postDelayed({
when (item.itemId) {
R.id.nav_privacy_policy -> {
val action = FragmentDirections.actionFragmentToPrivacyFragment("Policy link")
findNavController().navigate(action)
}
}
}, DRAWER_NAVIGATION_DELAY)
return true
}
And in xml you can add argument to the recieving fragment, in this case
<fragment
android:id="#+id/nav_privacy_policy"
android:name=".fragment.PrivacyPolicyFragment"
android:label="#string/menu_privacy_policy"
tools:layout="#layout/fragment_privacy_policy">
<argument
android:name="policy"
app:argType="string" />
</fragment>
You can also pass serializable objects, enum values and arrays of primitive types.
For example:
enum class ObjectType : Serializable {
FIRST, SECOND
}
Then, add arguments to the xml
<fragment
android:id="#+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="#string/menu_my_profile"
tools:layout="#layout/fragment_profile" >
<argument
android:name="myObjectType"
android:defaultValue="SECOND"
app:argType="com.project.app.data.ObjectType" />
</fragment>
Note, that you should specify complete path!
Passing data from the start destination with NavController NavGraph navigate is straightforward. I use this to display order lines associated to an order header:
private void showRepositionLinesFragment(AppObjects.RepOrderHeader orderHeader) {
int number = orderHeader.getOrderNumber();
String orderNumber = String.format("%06d",number);
String createDate = orderHeader.getCreateDate();
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,
"Navigate to FragRepoLines with orderNumber: " + orderNumber,false);
NavController navController = NavHostFragment.findNavController(FragmentRepositionHeaders.this);
Bundle bundle = new Bundle();
bundle.putString(getString(R.string.arg_header_ordernumber),orderNumber);
bundle.putString(getString(R.string.arg_repheader_createdate), createDate);
navController.getGraph().findNode(R.id.FragRepoLines).setLabel(orderNumber + " " + createDate);
navController.navigate(R.id.action_FragRepoHeaders_to_FragRepoLines,bundle);
}
Getting data from the fragment that handles the order lines turned to be more complicated. Tried for hours with NavController getArguments().
In the end this is what worked for me.
In the start fragment:
NavController navController = NavHostFragment.findNavController(this);
// We use a String here, but any type that can be put in a Bundle is supported
MutableLiveData<String> liveData = navController.getCurrentBackStackEntry()
.getSavedStateHandle()
.getLiveData(getString(R.string.arg_header_ordernumber));
liveData.observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(String s) {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info, "+++++++++ liveData changed -> " + s, false);
}
});
In the destination fragment:
String arg = getString(R.string.arg_header_ordernumber);
NavController navController = NavHostFragment.findNavController(this);
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
if (navBackStackEntry != null) {
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
if (savedStateHandle != null) {
savedStateHandle.set(arg, "000000");
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"savedStateHandle == null",false);
}
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"navBackStackEntry == null",false);
}
Source: Interact programmatically with the Navigation component
I changed the navController.getPreviousBackStackEntry() for navController.getCurrentBackStackEntry()
I had the same issue but I´m still not able to pass the arguments using fragment directions. Since I need the value in several of my fragments I decided to use a companion object in my main activity. It´s probably not the best but it solves the problem:
class MainActivity : AppCompatActivity() {
companion object{
var myGlobalVar = "Example"
}
override fun onCreate(savedInstanceState: Bundle?) {....
Then I can access its value in all of my fragments by importing it:
import myAppPackage.MainActivity.Companion.myGlobalVar
I had to delete the argument from my navGraph but i can still access it in the background.
I am rewriting my simple UI app to use Navigation architecture component, I need to pass a Pojo that implements Parcelable, have not seen any doc on how to do that.
Any help would be appreciated.
Since safe-args-gradle-plugin:1.0.0-alpha03 you can use Parcelable objects by using their fully qualified class name:
<argument
android:name="item"
app:argType="com.example.app.model.Item"/>
Parcelable arguments are now supported, using a fully qualified class name for app:type. The only default value supported is "#null" (https://issuetracker.google.com/issues/79563966)
Source: https://developer.android.com/jetpack/docs/release-notes
To support nullability one has to use android:defaultValue="#null" with app:nullable="true".
I know the answer is already there but this may help someone. Code snippet
In build.gradle add this dependancy
ext{
...
navigation_version = '1.0.0-alpha11'
}
dependencies {
...
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
}
In app/build.gradle
apply plugin: 'androidx.navigation.safeargs'
...
In Navigation graph
<fragment
android:id="#+id/source_fragment_id"
android:name="app.test.SourceFragment"
android:label="#string/source_fragment_label"
tools:layout="#layout/source_fragment_layout">
<action
android:id="#+id/action_source_fragment_to_destination_fragment"
app:destination="#id/destination_fragment_id"
...
/>
</fragment>
<fragment
android:id="#+id/destination_fragment_id"
android:name="app.test.DestinationFragment"
android:label="#string/destination_fragment_label"
tools:layout="#layout/destination_fragment_layout">
<argument
android:name="variableName"
app:argType="app.test.data.model.CustomModel" />
...
</fragment>
Note: CustomModel should be Parcelable or Serializable.
When navigating to this DestinationFragment from SourceFragment
val direction = SourceFragmentDirections.ActionSourceFragmentToDestinationFragment(customModel)
findNavController().navigate(direction)
Now retrieving the value from bundle in DestinationFragment
...
import app.test.DestinationFragmentArgs.fromBundle
class DestinationFragment : Fragment() {
val variableName by lazy {
fromBundle(arguments!!).variableName
}
...
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.e(DESTINATION_FRAGMENT_TAG,"onCreateView")
//Can use CustomModel variable here i.e. variableName
}
}
Right now you can't use safe args with types apart from integer, string, inferred and reference, there's an issue opened asking for other types.
What you can do now is to normally pass a bundle when using the navigate() method to navigate to a destination:
var bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)
And you can use the usual getArguments (or just arguments in kotlin) to retrieve that:
val tv = view.findViewById(R.id.textViewAmount)
tv.text = arguments.getString("amount")
You can use boolean, reference, integer, long, string, enum, parcelable and even serializable. But forget about the last one ;-)
Better use the latest plugin version safe-args-gradle-plugin:1.0.0-alpha08 and specify the fully qualified classname:
<fragment
...>
<argument
android:name="data"
app:argType="com.example.ParcelableData" />
</fragment>
from your
package com.example
data class ParcelableData(val content: String) : Parcelable { ... }
And you can send arrays of all the argTypes:
<argument
android:name="data"
app:argType="string[]" />