Pass argment to a nested graph's startDestination - android

I've created multiple graphs in separate files.
The first one is GraphA and it's includes GraphB. the startDestination of GraphB is FragmentB that has an argument named AnID
Now I want to pass the AnID from GraphA to GraphB (FragmentB)
Despite of the editor know about the argument but generated codes don't regard the argument:
val directionB = FragmentADirections.actionFragmentAToGraphB(/* HAS NOT ARGUMENT */)
findNavController().navigate(directionB)
How can I pass the argument to a nested graph?

I found this but it's not an official solution:
val direction = FragmentADirections.actionFragmentAToGraphB()
findNavController().navigate(direction.actionId, FragmentBArgs(anId).toBundle())
Update:
someone introduced another way by defining nested-graph arguments in action

Related

How to pass a Fragment as an argument

A summary of what I wanted to do: I want to create an app with tabs but I'll let the users have a setting where they can choose which couple of tabs do they want to use and in what order would they be.
So I thought I would create an ArrayList of all the possible Fragments and make a new ArrayList based on the user's settings. Now, I created a data class to store some details related to the Fragments, which I also set as the type for the ArrayList, and realized that I can't just pass a Fragment as an argument. Passing a regular Fragment makes Android Studio complain that I would want to use a [FragmentName].Companion instead. Accepting the suggestion makes the parameter exactly equal to [FragmentName].Companion, so I can't use it for other Fragments.
So, how do I do that? Or if you got better ideas on how I could structure the app, please let me know.
I would recommend passing only information about how to create fragments, for example, you can go with creating some FragmentDefinition class that will contain more data required to properly create fragments. for example class name, and bundle with parameters. So you can easily instantiate fragments without needing to know additional details.
data class FragmentDefinition(val className: String, val parameters: Bundle): Parcelable
fun sample(){
val fragmentDefinitions = listOf(
FragmentDefinition("com.sample.FragmentA", Bundle()),
FragmentDefinition("com.sample.FragmentB", Bundle()),
FragmentDefinition("com.sample.FragmentC", Bundle())
)
fragmentDefinitions.forEach { definition ->
val fragment = supportFragmentManager.fragmentFactory.instantiate(this::class.java.classLoader!!,definition.className)
fragment.arguments = definition.parameters
// add your fragment to hierarchy
}
}

Jetpack Compose Optional Arguments: What's the string between the braces {}?

When working with optional arguments in Compose: What's the value between the braces?
"item=" is the key. What is the purpose of the string within the braces?
composable("Details?item={item}",
arguments = listOf(navArgument("item") {
this.type = NavType.StringType
defaultValue = "Item not available!"
})) {
You have it backwards - the name of the query parameter (the item= part) could be anything. It could be lorem_ipsum or foobar or any word - that word isn't used by Navigation at all for figuring out how to parse that argument. The only thing that matters is the structure of your route pattern - that query parameter syntax is how you mark a parameter as an optional argument. Just like a web URL, you could navigate to Details as well as Details?item=cat - both would match your route. A required parameter would be part of the path (i.e., Details/{item}).
As explained in the Navigate with arguments guide:
Navigation compose also supports passing arguments between composable destinations. In order to do this, you need to add argument placeholders to your route, similar to how you add arguments to a deep link when using the base navigation library
That {item} is the placeholder that indicates that whatever string is in the place of the {item} should be parsed and put as an argument with the name item. This is how you can call navBackStackEntry.arguments?.getString("item") and get that parsed value out of the arguments of the destination.

How to pass argument in Android Navigation using Kotlin DSL

I have created my NavGraph using the Kotlin DSL and everything is fine. But I'm struggling to pass a simple argument between destinations.
I'm folowing this Android Docs without success: https://developer.android.com/guide/navigation/navigation-kotlin-dsl#constants
Part of graph that adds the argument as the docs says:
fragment<RestaurantsTabsFragment>(
"${CampusSelectorDestinations.restaurantsTabsFragment}/" +
CampusSelectorArguments.campusId
) {
argument(CampusSelectorArguments.campusId) {
type = NavType.StringType
defaultValue = "test"
}
}
Code with the navigation action trying to pass a argument:
campusesAdapter.onCampusClick = { campusId ->
findNavController().navigate("${CampusSelectorDestinations.restaurantsTabsFragment}/" + campusId
}
Error I get:
IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/restaurantsTabsFragment/jCkuLbzRHtW0CUzDFWYw } cannot be found in the navigation graph NavGraph
Can anyone help me? I can provide more information if needed
The pattern to pass the argumet route is wrong at the docs:
For luck, I've found this explanation inside a Navigation Lib class and that solved my problem (after 2 days struggling):
...
In addition to a direct Uri match, the following features are supported:
Uris without a scheme are assumed as http and https. For example, www.example.com will match http://www.example.com and https://www.example.com. Placeholders in the form of {placeholder_name} matches 1 or more characters. The String value of the placeholder will be available in the arguments Bundle with a key of the same name. For example, http://www.example.com/users/{id} will match http://www.example.com/users/4. The .* wildcard can be used to match 0 or more characters.
These Uris can be declared in your navigation XML files by adding one or more elements as a child to your destination.
...
Hope someone from Google see this and fixes the docs. (or explain if I'm wrong)
Just put your arguments into curved breaks and separate them by slash as it shown in the example below.
Define your destination with all required argument:
fragment<TransactionFragment>("${MainNavRoute.transaction}/{arg1}/{arg2}") {
argument("arg1") {
type = NavType.StringType
}
argument("arg2") {
type = NavType.LongType
}
Navigation to the destination:
findNavController().navigate("${MainNavRoute.transaction}/string_value/2")
Also, I have reported an issue to the tracker too. https://issuetracker.google.com/issues/221895357

How to transfer data from one Fragment to another?

I am writing an application on Kotlin (Android Studio), using jetpack.navigation architecture.
There are two fragments: The first contains a list with class instances, which I display in the RecyclerView, the second for EditText (I fill in the client data). I also use Livedata and ViewModel.
The problem is that when I go to the second fragment, fill in the data and confirm, I go to the 1st fragment. As I understand it, the following lines destroy the old Fragment1, and create a new one. the list on the first fragment is reset to zero (although the list is saved when you rotate the screen and minimize the application).
val client = Clients(id,name,secondName,thirdName, address, creditCard, bankNum)
val action = Fragment2Directions.actionFragment2ToFragment1(client)
findNavController().navigate(action)
I could not find how to solve problem using the navigation component. I will be very grateful.
To pass data between two fragments with jetpack navigation you have to use Safe Args
pass an argument section like
<fragment android:id="#+id/myFragment" >
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="0" />
</fragment>
add classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" in top level gradle file
and add the plugin apply plugin: "androidx.navigation.safeargs.kotlin"
now send the value like so
override fun onClick(v: View) {
val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
val amount = amountTv.text.toString().toInt()
val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
v.findNavController().navigate(action)
}
and receive it as
val args: ConfirmationFragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val tv: TextView = view.findViewById(R.id.textViewAmount)
val amount = args.amount
tv.text = amount.toString()
}
However safeargs works only for primitive types so you have to deconstruct and reconstruct if you're trying to pass Objects
As you are using ViewModels, I recommend that you use a shared ViewModel. The way it works is that multiple Fragments within the same Activity have access to the same ViewModel instance.
There is an example on Android Developers that fits your use case exactly. It shows how to use a shared ViewModel to do this using the master-detail navigation pattern and LiveData. I recommend you take a look at it.
Why not to use Safe Args here: You can try using Safe Args to achieve what you are trying to achieve, but I strongly recommend against it: You would have to deal with somehow using Safe Args to pass your Client objects between the Fragments back and forth (which means either sending each field individually or bundling) and you would have to manually update your LiveData objects - which defeats the purpose of LiveData. Using a shared ViewModel, you do not have to worry about any of that. No sending data back and forth, no taking your Client objects apart or bundling, no manual updating of LiveData objects - you simply access the same LiveData instance from both Fragments through your ViewModel.

Android Navigation Component: How to add a destination programmatically?

I want to inject a new destination to the current nav graph.
I notice NavGraph has a method void addDestination(#NonNull NavDestination node) but I can't find a proper way to create a NavDestination and navigate to it using navController.navigate(R.id.new_dest_id).
I've got two working ways:
navController.graph.addDestination(ActivityNavigator(this).createDestination().apply {
id = R.id.new_dest
setComponentName(ComponentName(context, NewActivity::class.java))
// or setIntent
})
or this
navController.graph.addDestination(
navController.navigatorProvider.getNavigator(ActivityNavigator::class.java)
.createDestination().apply {
id = R.id.new_dest
setComponentName(ComponentName(context, NewActivity::class.java))
}
)
There is also a DSL builder ActivityNavigatorDestinationBuilder.
Fragment is similar. Change ActivityNavigator to FragmentNavigator and use different setters.
I also made a mistake when I added a destination in one graph and tried to navigate to the new destination in another graph. of course that never work.
Did you try to pass the ID of your newly created Destination in navigate(), like navigate(myDes.getId())

Categories

Resources