Using DialogFragment as one of the Fragment in Android NavController - android

I have a graph defined using XML and I added a DialogFragment as one of the Fragment in my NavGraph. when I call NavController.navigate with resId and bundle on that nav controller I don't see any Dialog being displayed. Is there any way I can use DialogFragment instead of standard Fragment?
<fragment
android:id="#+id/noLoginDialog"
android:name="com.ram.view.NoLoginDialog"
android:label="NoLoginDialog">
<argument
android:name="argTitle"
android:defaultValue="null"
app:type="string"/>
<argument
android:name="argBody"
android:defaultValue="null"
app:type="string"/>
<argument
android:name="argButton"
android:defaultValue="null"
app:type="string"/>
</fragment>
and my action is defined as below
<container_fragment
android:id="#+id/homeFragment"
android:name="com.ram.home.HomeFragment"
android:label="HomeFragment">
<action
android:id="#+id/action_homeFragment_to_noAuthAlertDialog"
app:destination="#id/noLoginDialog"/>
</container_fragment>
My other actions with activity and fragments works just fine.

hum, Navigation Library will never open an Fragment as Dialog, it just replace the first (home destination) to other destination in your NavHostFragment.
Google said:
A destination is any place you can navigate to in your app. While
destinations are usually Fragments representing specific screens..
Please provide more information, like your navigation code (java/kotlin).
Take a read on this official Google post to understand more about Navigation:
https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing

Yes you can call up DialogFragment from navigation, you will just have to explicitly tell the navigation that you are going to use dialog
<dialog
android:id="#+id/simCancelFinish"
android:name="mk.telekom.kiosk.ui.dialogs.SimCancelFinish"
android:label="SimCancelFinish" >
<action
android:id="#+id/action_simCancelFinish_to_stornoSimFragment"
app:destination="#id/stornoSimFragment"
app:popUpTo="#id/stornoSimFragment"
app:popUpToInclusive="true" />
</dialog>

Related

Navigation graph with viewpager2 android

My viewpager consists of 2 fragments which are displayed correctly and working fine.
From viewpager's fragment I want to move to another fragment via navigation graph
This is my 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/offer_nav_graph"
app:startDestination="#id/offersFragment">
<fragment
android:id="#+id/offersFragment"
android:name="com.octave.offers.OffersFragment"
android:label="offers_fragment"
tools:layout="#layout/offers_fragment" />
<fragment
android:id="#+id/availableOfferDetailFragment"
android:name="com.octave.offers.available.AvailableOfferDetailFragment"
android:label="fragment_available_offer_detail"
tools:layout="#layout/fragment_available_offer_detail" />
<fragment
android:id="#+id/availableOffersFragment"
android:name="com.octave.offers.available.AvailableOffersFragment"
android:label="fragment_available_offers"
tools:layout="#layout/fragment_available_offers" >
<action
android:id="#+id/action_availableOffersFragment_to_availableOfferDetailFragment"
app:destination="#id/availableOfferDetailFragment" >
<argument
android:name="offerId"
app:argType="integer"
android:defaultValue="-1" />
</action>
</fragment>
</navigation>
Offers fragment - has view pager
Available offer fragment - one of the fragment on view pager
Available offer detail fragment - i want to navigate
On button click I am calling this
AvailableOffersFragmentDirections.actionAvailableOffersFragmentToAvailableOfferDetailFragment(offerId)
The exception
Navigation action/destination com.octave.staging:id/action_availableOffersFragment_to_availableOfferDetailFragment cannot be found from the current destination Destination(com.octave.staging:id/homeFragment) label=HomeFragment class=com.octave.home.HomeFragment
What is wrong over here?
button exists on availableOffersFragment. I want to navigate from
availableOffersFragment to availableOfferDetailFragment.
offersFragment has the viewpager with 2 tabs having their own fragment
availableOffersFragment and secondFragemnt
So, now you need to navigate from availableOffersFragment which is a tab (page fragment) in the ViewPager; but this is not possible because those tabs don't affect the back stack. You can check out the questions discussed in here and also this one.
But what you can do instead is to add an action from offersFragment (which holds the ViewPager) to availableOfferDetailFragment as following:
Remove the tab fragments from the navGraph (availableOffersFragment and secondFragemnt).
Create an action in the navGraph from offersFragment to availableOfferDetailFragment
<fragment
android:id="#+id/offersFragment"
android:name="com.octave.offers.OffersFragment"
android:label="offers_fragment"
tools:layout="#layout/offers_fragment">
<action
android:id="#+id/action_offersFragment_to_availableOfferDetailFragment"
app:destination="#id/availableOfferDetailFragment" >
<argument
android:name="offerId"
app:argType="integer"
android:defaultValue="-1" />
</action>
</fragment>
When the button is clicked use parentFragment to access the offersFragment from the availableOffersFragment tab when the button is clicked. For instance:
Create a method in offersFragment:
goToDetailsFragment(offerId: Int) {
// perfrom the navaigation
OffersFragmentDirections.actionOffersFragmentToAvailableOfferDetailFragment(offerId)
}
And in availableOffersFragment
btn.setOnClickListener {
(parentFragment as offersFragment).goToDetailsFragment(offerId)
}
Check using:
btn.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.availableOfferDetailFragment)
}

How to reuse a fragment in different navigation graphs with safe args enabled?

I'm trying to reuse a fragment in different navigation graphs with safe args enabled. I noticed that if the actions are different I get a compilation error. This is because the xxxFragmentDirections autogenerated code will only generate one of the actions.
In nav_graph_1.xml:
<navigation
...
<fragment
android:id="#+id/myFragment"
android:name="com.example.android.MyFragment">
<action
android:id="#+id/next_action"
app:destination="#+id/dest_one" />
</fragment>
...
In nav_graph_2.xml:
<navigation
...
<fragment
android:id="#+id/myFragment"
android:name="com.example.android.MyFragment">
<action
android:id="#+id/other_action"
app:destination="#+id/other_dest" />
</fragment>
...
A simple use case: A bank app that has two flows: withdraw and deposit, therefore you could have two nav graphs. You could have an AmountFragment where you could just enter a number and this could be reused to either withdraw or deposit. However, depending on the flow, the actions/destinations could be different.
Then, how would it be possible to reuse this fragment?
Use navigate() with bundle instead of actions in edge cases. Don’t call
findNavController().navigate(FragmentDirections.goToDetailFragment(id))
but instead use
findNavController().navigate(R.id.DetailFragment, bundleOf("id" to 5))
This way you don’t rely on the generated direction but can still use the Navigation and SafeArgs features of the DetailFragment.
https://code.allaboutapps.at/articles/android-jetpack-navigation-pitfalls/#reuse-fragments-in-multiple-navigation-graphs
This can be achieved by providing the same action ID in both the navigation graph.
In nav_graph_1.xml:
<navigation
...
<fragment
android:id="#+id/myFragment"
android:name="com.example.android.MyFragment">
<action
android:id="#+id/next_action"
app:destination="#+id/dest_one" />
</fragment>
...
In nav_graph_2.xml:
<navigation
...
<fragment
android:id="#+id/myFragment"
android:name="com.example.android.MyFragment">
<action
android:id="#+id/next_action"
app:destination="#+id/other_dest" />
</fragment>
...
In fragment, navigation can be performed like
NavHostFragment.findNavController(<myFragment>).navigate(R.id.next);
It might not be what you ask, but I faced the problem o re-usability of code in a fragment that I needed in different navigation graphs.
The fragment class that needs to be reused should be declared as abstract:
abstract class ReusableFragment(val type: ReusableEnum): Fragment(){
enum class ReusableEnum{Type1, Type2}
// the rest of your logic will use
// 'type' variable in when() blocks to determine
// specific logic for each case
}
class Type1Fragment: ReusableFragment(ReusableEnum.Type1)
class Type2Fragment: ReusableFragment(ReusableEnum.Type2)
This way, Type1Fragment and Type2Fragment are available in NavigationGraph as independent fragments.

Is it possible to start with a "non-start" fragment using Android Navigation Architecture Component(Android Jetpack)?

so I have the following navigation graph:
Fragment A (start) --> Fragment B
So for some situations (firebase notifications), I need to start Fragment B directly, passing data from the notifications. Now, this works. However, when I press the back button, it results in a crash. Is it because the leading fragment (Fragment A) is not in the stack? If so, is there a way to properly handle this.
Basically, I need the backPressed action to launch the start Fragment (Fragment A) in a situation where Fragment B is launched directly without passing through Fragment A.
Below is a snippet of my graph:
<fragment
android:id="#+id/homeFragment"
android:name="dita.dev.myportal.ui.home.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/action_homeFragment_to_messageDetailFragment"
app:destination="#id/messageDetailFragment"
app:exitAnim="#anim/fade_out_animation" />
</fragment>
<fragment
android:id="#+id/messageDetailFragment"
android:name="dita.dev.myportal.ui.messages.details.MessageDetailFragment"
android:label="Message"
tools:layout="#layout/fragment_message_detail">
<argument
android:name="title"
app:argType="string" />
<argument
android:name="message"
app:argType="string" />
</fragment>
I hope the link helps.
https://developer.android.com/guide/navigation/navigation-deep-link
Deep links allow for synthetic back-stacks.
Quoted from the website, "This means that when a user presses the Back button from a deep link destination, they navigate back up the navigation stack just as though they entered your app from its entry point."
Maybe trying to create a deep link for Fragment B will work.

Android NavigationUI - Handle backpress when starting fragment is poped out

I am using NavigationUI of android architecture components with bottom navigation in my android app. I have the following situation
The app starts at Fragment A
Initial calculation is done and now I pop out Fragment A by navController.popBackStack()
The app goes to Fragment B (This serves as a 'Home' for the app)
Now there are more fragments say C and D
The user can navigate between B, C and D
Now the problem is in my navigation graph, my starting fragment was A (by defining app:startDestination="#id/fragmentA") but that is now pop out of the activity. Due to this, anytime if I backpress, the app just closes instead going back to the previous fragment. For example, let's say I navigate to Fragment C or D from fragment B and if I press back button, my app will close instead of going to fragment B. If the there way to 'reassign' the startDestination?
I checked the answer to this question which has a similar situation as above but using NavOptions is somehow not working. Is there any other way? Following is the code I used to pop fragment and add nav options
navController.popBackStack()
val navOptions: NavOptions = NavOptions.Builder()
.setPopUpTo(R.id.homeFragment, true)
.build()
navController.navigate(R.id.action_fragmentA_to_fragmentB, null, navOptions)
Use this navigation graph. Hope it will solve your problem.
<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/fragment_a">
<fragment
android:id="#+id/fragment_a"
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_a_to_b"
app:destination="#id/fragment_b"
app:popUpTo="#id/fragment_a"
app:popUpToInclusive="true"/>
</fragment>
<fragment
android:id="#+id/fragment_b"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/action_b_to_c"
app:destination="#id/fragment_c"/>
</fragment>
<fragment
android:id="#+id/fragment_c"
tools:layout="#layout/fragment_c">
</fragment>
</navigation>
Use the following line for navigation:
navController.navigate(R.id.action_a_to_b)
navController.navigate(R.id.action_b_to_c)

Passing an argument between two fragments each of them in a different Navigation Graph

Using Android Navigation Component. I have two Navigation Graphs (each of them with their own flow), lets say navigation graphs A and B. I navigate successfully from A to B, but I can't manage to pass an argument from the last Fragment included in graph A to the start Fragment that belongs to graph B.
I am able to pass arguments between fragments that belong to the same graph, but the function to set the arguments is not been generated when navigating between to navigation graphs.
I am trying to accomplish that using safeargs.
Here is the navigation graphs code:
Navigation Graph A:
<navigation android:id="#+id/nav_graph_a"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment1"
android:name="com.mypackage.fragments.Fragment1"
android:label="Fragment1">
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"/>
</fragment>
<fragment
android:id="#+id/fragment2"
android:name="com.mypackage.fragments.Fragment2"
android:label="Fragment2">
<argument
android:name="thisArgumentAlwaysArrive"
android:defaultValue="null"
app:argType="string"/>
<action
android:id="#+id/action_fragment2_to_nav_graph_b"
app:destination="#id/nav_graph_b"/>
</fragment>
<include app:graph="#navigation/nav_graph_b"/>
Navigation Graph B:
<navigation android:id="#+id/nav_graph_b"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:startDestination="#id/fragment3">
<fragment
android:id="#+id/fragment3"
android:name="com.mypackage.fragments.Fragment3"
android:label="Fragment3">
<argument
android:name="thisArgumentNeverArrive"
app:argType="string"/>
</fragment>
Any idea on how to accomplish this?
As stated in the documentation:
Note: Safe Args do not support cross-module navigation, as there is no direct action to the destination. In the previous example, although a Directions class would be generated for the target destination in settings, you aren't able to access the generated class from the classpath of the list module.
But you can use a bundle instead of nav_args like this:
From
FristFragmentDirections.actionFirstFragmentToSecondFragment(your_parameter).let { action ->
findNavController().navigate(action)
}
To
FristFragmentDirections.actionFirstFragmentToSecondFragment().let { action ->
findNavController().navigate(
action.actionId,
Bundle().apply { putString("parameter_key", your_parameter) }
)
}
Safe Args is the plugin for Gradle that creates the classes ClassArgs for the destinations.
The default controller provides a way to navigate to another NavGraph, in this case there is an implementation of a Navigator called NavGraphNavigator.
This navigator provides you with a navigate(NavGraph destination,Bundle args) function that passes the arguments to the start fragment.
So, the best way I have found to do what you are trying to achieve is to use the generated class to generate a bundle with the needed arguments.
var myBundle = YourFragmentIdInNavGraphArgs.Builder(var args..).toBundle()
and then use the default NavController to navigate to a navGraph destination like this.
view.findNavController().navigate(R.id.your_action_to_nav_graph_id,myBundle)
the implementation of the Navigator will take care of passing the arguments to the start fragment on the destination.
Hope it helps to some one.

Categories

Resources