Let DialogFragment in navigation not disappear on poping back stack - android

I have FragmentA, FragmentB and DialogFragment(BottomDialogFragment). I I abbreviated them as A,B and D
D will be shown after the button in A is clicked. It means A -> D
B will be shown after the button in D is clicked. It means D -> B
I config them in navigation.xml
<fragment
android:id="#+id/A"
android:name="com.example.A">
<action
android:id="#+id/A_D"
app:destination="#id/D" />
</fragment>
<dialog
android:id="#+id/D"
android:name="com.example.D">
<action
android:id="#+id/D_B"
app:destination="#id/B" />
</dialog>
<fragment
android:id="#+id/B"
android:name="com.example.B">
</fragment>
Now when I click the button in A, the fragment will jump to D.
Then I click the button in D, the fragment will jump to B.
But when I pop the navigation stack in B, it will back to A, and the D doesn't show.
What should I do? I want the D still exists on the surface of A.

What should I do? I want the D still exists on the surface of A.
So far, this is not possible, because dialogs are handled in a separate window than the activities/fragments; and therefore their back stack is handled differently And this is because Dialog implements the FloatingWindow interface.
Check this answer for more clarification.
But to answer your question, there are two approaches in mind:
Approach 1: Change fragment B to a DailogFragment, and in this case both B & D are dialogs and therefore when you popup the stack to back from B to D, you'll still see D showing.
Approach 2: To have a flag that is set if you return from B to D, and when if so, you re-show D.
Actually, approach 2 isn't that good, because it doesn't keep D in the back stack while you go from D to B; it's just a workaround; also the user would see the dialog transitioning/fade animation while it returns from B to D; so it's not natural at all. So, here only approach 1 will be discussed.
Approach 1 in detail:
Pros:
It's very natural and will keep the back stack the same as you would like to.
Cons:
DialogFragment B has a limited window than the normal fragment/activity.
B is no longer a normal fragment, but a DialogFragment, so you might encounter some other limitations.
To solve the limited window of B, you can use the below theme:
<style name="DialogTheme" parent="Theme.MyApp">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowIsFloating">false</item>
</style>
Where Theme.MyApp is your app's theme.
And apply it to B using getTheme():
class FragmentB : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return layoutInflater.inflate(R.layout.fragment_b, container, false)
}
override fun getTheme(): Int = R.style.DialogTheme
}
Also you need to change B in the navigation graph to a dialog:
<dialog
android:id="#+id/B"
android:name="com.example.B">
</dialog>
Preview:

Refer to this link for a full working example.
You need to leverage the NavigationUI global action (as explained here) in order to be able to navigate "back" to a destination. Put this code into the main_graph xml:
<action android:id="#+id/action_global_fragmentD" app:destination="#id/fragmentD"/>
Next, in your activity add these to catch back press:
class MainActivity: AppCompatActivity {
...
var backPressedListener: OnBackPressedListener? = null
override fun onBackPressed() {
super.onBackPressed()
backPressedListener?.backHaveBeenPressed()
}
}
interface OnBackPressedListener {
fun backHaveBeenPressed()
}
this is like delegates in swift
Next in FragmentB, add these:
class FragmentB: Fragment(), OnBackPressedListener {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
(activity as MainActivity).backPressedListener = this
return inflater.inflate(R.layout.fragment_b, container, false)
}
override fun backHaveBeenPressed() {
// show Dialog
findNavController().navigate(R.id.action_global_fragmentD)
}
}
You can then navigate back to the DialogFragment as you require. This approach does not use the popBackStack because your use case is a custom behavior not handled by the NavigationUI framework (you need to implement it).

There is no need to pop the stack with controller.popBack() as the Stack is managed by the Navigation Library. Note that the stack operates on a LIFO basis so that is why the Fragment disappeared.
You need to add more navigation graph actions:
<?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"
android:id="#+id/navigation"
app:startDestination="#id/A">
<fragment
android:id="#+id/A"
android:name="com.example.A"
android:label="A" >
<action
android:id="#+id/action_A_to_D"
app:destination="#id/D" />
</fragment>
<dialog
android:id="#+id/D"
android:name="com.example.D"
android:label="D" >
<action
android:id="#+id/action_D_to_B"
app:destination="#id/B" />
<action
android:id="#+id/action_D_to_A"
app:destination="#id/A" />
</dialog>
<fragment
android:id="#+id/B"
android:name="com.example.B"
android:label="B" >
<action
android:id="#+id/action_B_to_D"
app:destination="#id/D" />
</fragment>
</navigation>
Then in your dialog Fragment add the following:
For OK/Yes:--
private fun doNav() {
NavHostFragment.findNavController(this).navigate(R.id.action_fragmentD_to_fragmentB)
}
For CANCEL/No:--
private fun doBackNav() {
NavHostFragment.findNavController(this).navigate(R.id.action_fragmentD_to_fragmentA)
}
Finally in the B Fragment, override the back press button and execute this:
Navigation.findNavController(requireView()).navigate(R.id.action_fragmentB_to_fragmentD)

Related

Android Navigation - Removing Action Bar Back Button When Popping Back Stack

What I'm trying to do
I am using Android Navigation component to handle navigation in my app. In this example, I have two screens, screen A and screen B.
I want the user to be able to click a button in screen A and be able to navigate to screen B; and then be prevented from going back to the previous screen (screen A).
The problem
When the user navigates to screen B from screen A, the back button on the action bar still allows the user to go back to the previous screen, however when clicking on the back button in the bottom bar it exits the app so this part works OK.
What do I need to do in order to remove the back button in the Action Bar?
What I've read so far
I have followed the guidance within these three articles but I think they might be ignoring the ActionBar's back button:
Stackoverflow - How to clear navigation Stack after navigating to
another fragment in Android
Android Developer Guide - Conditional navigation
Android Developer Guide - Navigate to a destination
My Code
Navigation Graph - nav_graph.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"
android:id="#+id/nav_graph"
app:startDestination="#id/screen_a">
<fragment
android:id="#+id/screen_a"
android:name="com.example.conditionalnavigation.AFragment"
android:label="screen A">
<action
android:id="#+id/action_AFragment_to_BFragment"
app:destination="#id/screen_b"
app:launchSingleTop="true"
app:popUpTo="#id/screen_a"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/screen_b"
android:name="com.example.conditionalnavigation.BFragment"
android:label="screen B" />
</navigation>
MainActivity - This acts as my Single Activity navhost.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
}
In your activity class add the following member (in Kotlin):
private lateinit var appBarConfiguration: AppBarConfiguration
Inside the onCreate method add the following lines:
....
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
...
...
appBarConfiguration = AppBarConfiguration(
setOf([**ID of the fragment layout you want without back button**],
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
....
In this way your fragment will be a root fragment and the back button is removed. Hope it helps.
Try to disable home button at the creation of screen b fragment:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
var rootView = inflater?.inflate(R.layout.fragment_screen_b, container, false)
(activity as AppCompatActivity).supportActionBar!!.setDisplayHomeAsUpEnabled(false)
return rootView
}
If it didn't work, then try it in onViewCreated() method.
If not worked, try to add below as well:
setHasOptionsMenu(false)

Navigate between different graphs with Navigation components

I have two activities, one holds all the fragments for the Login process, and the other one holds all the fragments for the main app.
Let's say I want to navigate from Activity1 (that holds all navigation graph of Login) to Activity2 (That holds all the navigation graph for the main app)
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
}
fun goToMainActivity(){
startActivity(Intent(this,MainActivity::class.java))
finish()
}
}
Here I call the method goToMainActivity()
class LoginFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_login,container,false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_go.setOnClickListener {
// call the method goToMainActivity() to kill all fragments contained by that Activity and move foward to MainActivity with another nav_graph
}
}
}
Since LoginActivity holds a nav_graph and is the navigation host for all the Login Fragments, now I want to kill all the fragments contained to LoginActivity and move towards a new Activity (MainActivity) that holds a different nav graph
Is this the good way to do it? Or I should navigate differently ?
You don't need to define a second activity, simply add a second navigation graph to your nav_graph.xml file. Something like:
<?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/nav_graph"
app:startDestination="#id/loginFragment">
<fragment
android:id="#+id/loginFragment"
android:name="com.mycompany.loginFragment"
tools:layout="#layout/fragment_login"
android:label="Login" >
<action
android:id="#+id/action_loginFragment_to_new_graph"
app:destination="#id/new_graph" />
</fragment>
<include app:graph="#navigation/new_graph" />
</navigation>
Then, with your navController, navigate the action:
navController.navigate(R.id.action_loginFragment_to_new_graph)
You can migrate to a single Activity Navigation. In your Nav Graph add an Action to navigate between the last LoginFragemnt and MainFragment and select:
Pop Behaviour:
Pop To - Self
Inclusive - YES
This should automatically clear the stack for you and pressing back will close the App.
EDIT:
Or just manually add these two lines to your nav xml under the action that moves from the LoginFragment to the MainFragment:
app:popUpTo="#id/loginFragment"
app:popUpToInclusive="true"

Navigation popUpTo and PopUpToInclusive aren't clearing the backstack

I'm new to the Android Jetpack Navigation architecture. I'm trying it out on a new app. There's one activity and a few fragments, two of them are login screen and email login screen. I defined those fragments in my navigations XML. The flow of the app is as follows:
Login screen → Email Login screen
What I want is, after navigating to the email login screen, when I press back, the app exits. Meaning the back-stack for login screen is removed. I know login screens aren't supposed to work that way, but I'm still just figuring things out.
I followed the documentation from Google's Get started with the Navigation component. It said, using app:popUpTo and app:popUpToInclusive="true" is supposed to clear the backstack, yet when I press back on email login screen, it still goes back to login instead of exiting.
So, here's what I've tried.
nav_main.xml
<fragment android:id="#+id/loginFragment"
android:name="com.example.myapp.ui.main.LoginFragment"
android:label="#string/login"
tools:layout="#layout/fragment_login" >
<action
android:id="#+id/action_login_to_emailLoginFragment"
app:destination="#id/emailLoginFragment"
app:popEnterAnim="#anim/slide_in_right"
app:popExitAnim="#anim/slide_out_right"
app:popUpTo="#+id/emailLoginFragment"
app:popUpToInclusive="true"/>
</fragment>
<fragment android:id="#+id/emailLoginFragment"
android:name="com.example.myapp.ui.main.EmailLoginFragment"
android:label="EmailLoginFragment"
tools:layout="#layout/fragment_login_email" />
LoginFragment.kt
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding.emailLoginButton.setOnClickListener {
findNavController().navigate(R.id.action_login_to_emailLoginFragment)
}
return binding.root
}
I gave a click event to a button. In it, I used the Navigation Controller to navigate to the email login screen by giving it the action's ID. In the <action>, there are app:popUpTo and app:popUpToInclusive="true".
After reading the documentation over and over, as well as reading plenty of StackOverflow questions, I found those properties are supposed to remove my login screen off the back-stack. But they don't. The button does navigate to the email login screen, but when I press back, it still goes back to login screen instead of exiting the app. What am I missing?
<action
android:id="#+id/action_login_to_emailLoginFragment"
app:destination="#id/emailLoginFragment"
app:popEnterAnim="#anim/slide_in_right"
app:popExitAnim="#anim/slide_out_right"
app:popUpTo="#+id/loginFragment"
app:popUpToInclusive="true"/>
Your popUpTo is going back to the email login, and then popping it because of the inclusive.
If you will change the popUpTo to your login fragment, it will be navigated back to, and popped as well because of the inclusive flag, which will result in your desired behaviour.
I write this answer for people who have not completely understood the
way popUpTo works and I hope its example helps someone because most
examples for navigation are repetitive in most sites and do not show
the whole picture.
In any <action> if we write a value for app:popUpTo, it means we want to delete some of the fragments from the back stack just after completing the action, but which fragments are going to be removed from the back stack when action is completed?
Its order is Last In First Out so:
All fragments between the last fragment and the fragment defined in popUpTo will be removed.
And if we add app:popUpToInclusive="true", then the fragment defined
in popUpTo will also be removed.
Example:
Consider fragments from A to G in a navigation graph like this:
A->B->C->D->E->F->G
We can go from A to B and then from B to C and so on. Consider the following two actions:
An action E->F we write:
<action
...
app:destination="#+id/F"
app:popUpTo="#+id/C"
app:popUpToInclusive="false"/>
And for F->G we write:
<action
...
app:destination="#+id/G"
app:popUpTo="#+id/B"
app:popUpToInclusive="true"/>
Then after going from E to F using the action E->F, the fragments between the last fragment (F) and C (which is defined in popUpTo of E->F) will be removed. The fragment C will not be removed this time because of app:popUpToInclusive="false" so our back stack becomes:
A->B->C->F (F is currently on Top)
Now if we go to fragment G using action F->G :
all fragments between the last fragment(G) and B (which is defined in popUpTo of F->G ) will be removed but this time the fragment B will also be removed because in F->G action we wrote app:popUpToInclusive="true" . so back stack becomes:
A->G (G is on top now)
These 2 lines make the trick works:
If you want to go from A to B and expect to finish A:
You need to call B with this action:
<fragment
android:id="#+id/fragmentA"
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_call_B"
app:destination="#+id/fragmentB"
app:popUpTo="#id/fragmentA"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentB"
tools:layout="#layout/fragment_b">
</fragment>
If you put log to your fragments you can see that fragmentA is destroyed after calling fragmentB with this action.
You can do it in XML just like this answer does, or you can also do it programmatically:
NavOptions navOptions = new NavOptions.Builder().setPopUpTo(R.id.loginRegister, true).build();
Navigation.findNavController(mBinding.titleLogin).navigate(R.id.login_to_main, null, navOptions);
Let's say that your app has three destinations—A, B, and C—along with actions that lead from A to B, B to C, and C back to A. The corresponding navigation graph is shown in figure
With each navigation action, a destination is added to the back stack. If you were to navigate repeatedly through this flow, your back stack would then contain multiple sets of each destination (A, B, C, A, B, C, A, and so on). To avoid this repetition, you can specify app:popUpTo and app:popUpToInclusive in the action that takes you from destination C to destination A, as shown in the following example:
<fragment
android:id="#+id/c"
android:name="com.example.myapplication.C"
android:label="fragment_c"
tools:layout="#layout/fragment_c">
<action
android:id="#+id/action_c_to_a"
app:destination="#id/a"
app:popUpTo="#+id/a"
app:popUpToInclusive="true"/>
After reaching destination C, the back stack contains one instance of each destination (A, B, C). When navigating back to destination A, we also popUpTo A, which means that we remove B and C from the stack while navigating. With app:popUpToInclusive="true", we also pop that first A off of the stack, effectively clearing it. Notice here that if you don't use app:popUpToInclusive, your back stack would contain two instances of destination A
popUpTo its to define the place that you want to go when you press back. If you set popUpInclusive = true, the navigation skipe that place too ( in popUpTo ).
Sample: A -> B -> A
FragmentB.kt
Attempts to pop the controller's back stack
private fun popBackStackToA() {
if (!findNavController().popBackStack()) {
// Call finish on your Activity
requireActivity().finish()
}
}
Back Stack
I faced a similar problem and my approach was simple. In the navigation graph, you have to designate the starting screen. Mine was:
app:startDestination="#id/webview"
It's called the start destination, it is the first screen users see when opening your app, and it's the last screen users see when exiting your app.
If you do not wish your login activity to be shown as you exit the app, just remove it as the start destination and use the fragment that you wish to show last in your case, It's the Email Login screen.
<?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/nav"
app:startDestination="#id/Email Login screen">
Also, make sure you override the onBackPressed() method from the host activity code as:
override fun onBackPressed() {
finish()
super.onBackPressed()
}
Now that you have removed the login fragment as the start destination, it's now not obvious what fragment will be shown first when the app opens.
Add a method to implement that in the host activity and call it from the oncreate(). In my case,i created initContent() to handle that logic. This was the code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
if (savedInstanceState == null) {
initContent()
}
}
private fun initContent() {
if (isNetworkConnected()) {
navController.navigate(R.id.webView)
} else {
navController.navigate(R.id.noInternetFragment)
}
}
Hope this helps someone.

Android Jet Pack Navigation, setupWithNavController() recreating fragment

I have a problem with my navigation view using jetpack's BottomNavBar
so here's how my flow works.
I have 4 views and every one of them have redirections like when I'm in last selection of the navbar I have a fragment A -> fragment B and when I go back to the first selection of the navbar and when I go back to the 4th one its one the fragment A again. I believe it is because fragments are recreating using the setupWithNavController() if it so does jetpack have a workaround for that?
here is my code for some reference.
<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/splashFragment">
<fragment
android:id="#+id/selectionFragment"
android:name="whitecloak.com.allibuy.app.selection.SelectionFragment"
android:label="fragment_selection"
tools:layout="#layout/fragment_selection" >
<action
android:id="#+id/toLogin"
app:destination="#id/loginFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/nav_graph"
app:popUpToInclusive="true/>
</fragment>
<fragment
android:id="#+id/splashFragment"
android:name="whitecloak.com.allibuy.app.splash.SplashFragment"
android:label="fragment_splash"
tools:layout="#layout/splash_fragment"
>
<action
android:id="#+id/toMain"
app:destination="#id/mainFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/nav_graph"
app:popUpToInclusive="true"/>
</fragment>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/splashFragment"
android:icon="#drawable/home"
android:title="#string/home"
app:popUpTo="#id/nav_graph" />
<item
android:id="#+id/tabCart"
android:icon="#drawable/cart"
android:title="#string/cart"
app:popUpTo="#id/nav_graph" />
<item
android:id="#+id/tabNotif"
android:icon="#drawable/notification"
android:title="#string/notification"
app:popUpTo="#id/nav_graph"/>
<item
android:id="#+id/selectionFragment"
android:icon="#drawable/user"
android:title="#string/account"
app:popUpTo="#id/nav_graph" />
bottomNav.setupWithNavController(findNavController(R.id.nav_main))
I just included the XML for the 1st and last tab. Thank you so much.
EDIT
class MainNavigation : DaggerAppCompatActivity() {
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: MainNavigationViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this, viewModelFactory)[MainNavigationViewModel::class.java]
bottomNav.setupWithNavController(findNavController(R.id.nav_main))
}
}
It's not related to the setup of your BottomNav. It's rather the explicit behavior in the implementation made for Android. I'll quote and explain:
Behavior
On Android: the app navigates to a destination’s top-level screen. Any prior user interactions and temporary screen states are reset, such as scroll position, tab selection, and in-line search.
From https://material.io/design/components/bottom-navigation.html#behavior
This means that when you click an item on the BottomNav, it should always go back to the first fragment on this flow's stack.
If I'm not being clear, here is a pseudo-representation:
BottomNavItem#1 > Fragment1A > Fragment1B
BottomNavItem#2 > Fragment2A > Fragment2B
If you tap on BottomNavItem#1, it loads Fragment1A. Then imagine using a button it shows Fragment1B. If you now click on BottomNavItem#2, you'll see Fragment2A. Now, if you click back on BottomNavItem#1, it will show Fragment1A (not the Fragment1B that you saw last), because it's the root of that stack/flow.

How to disable UP in Navigation for some fragment with the new Navigation Architecture Component?

I am trying out the new Navigation Architecture Component, and I can't figure out how to do this:
I have 1 Activity (MainActivity) + 3 Fragments:
SplashFragment (Home)
MainFragment
SignUpFragment
I would like to use SplashFragment to determine if I should navigate to MainFragment or SignUpFragment, but once it reaches either of those 2, you should not be able to pop back to SplashFragment. How can I do that with the new navigation component?
I tried popBackStack before and after calling navigate(R.id.action_xxx), but neither of them work (which make sense: before it has nothing to pop; after it just closes the fragment that just got added). Does that mean the only way to do that is to override onBackPress to intercept it and make sure navigateUp does not get call in those cases?
Thanks!
First, add attributes app:popUpTo='your_nav_graph_id' and app:popUpToInclusive="true" to the action tag.
<fragment
android:id="#+id/signInFragment"
android:name="com.glee.incog2.android.fragment.SignInFragment"
android:label="fragment_sign_in"
tools:layout="#layout/fragment_sign_in" >
<action
android:id="#+id/action_signInFragment_to_usersFragment"
app:destination="#id/usersFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
Second, navigate to the destination, using the above action as parameter.
findNavController(fragment).navigate(SignInFragmentDirections.actionSignInFragmentToUserNameFragment())
NOTE: If you navigate using method navigate(#IdRes int resId), you won't get the desired result. Hence, I used method navigate(#NonNull NavDirections directions).
This worked for me in alpha05 release. Add app:popUpTo="#id/nav_graph" in the action tag(inside your nav_graph.xml file).
Here "#id/nav_graph is the id of my graph or also called as the Root.
<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/startFragment">
.......
<action
android:id="#+id/action_startFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#id/nav_graph"/>
.......
You can also do this in design tab:- select "SplashFragment" and select the action you want to change and then pick "root" for "Pop To"
WARNING: clearTask has been deprecated and will be remove in future release, not sure what the solution is. Please follow this issue for now to keep up to date
Oh after 10 minutes finally found the key: use clearTask.
All I have to do is add app:clearTask="true" to that specific action, or use .navigate(R.id.actionXXXX, null, NavOptions.Builder().setClearTask(true).build()), and it's done. Just make sure you add it to all the children of SplashFragment (in this case, both MainFragment and SignUpFragment).
So if you have splash fragment and main fragment and you don't want to go back to splash fragment after the main fragment below method you can achieve this
<fragment
android:id="#+id/splashFragment"
android:name="com.example.youappname.views.SplashFragment"
android:label="fragment_splash"
tools:layout="#layout/fragment_splash">
<action
android:id="#+id/action_splashFragment_to_mainFragment"
app:destination="#id/mainFragment"
app:popUpTo="#id/splashFragment"
app:popUpToInclusive="true"/>
</fragment>
In you Kotlin Splash Fragment:
private lateinit var navController: NavController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
}
private fun navigateToMainFrag() {
navController.navigate(R.id.action_splashFragment_to_mainFragment)
}
Now when you press back button it will close the app instead of showing the splash screen
For anyone wanted to do this purely in code:
Navigation.findNavController(v)
.navigate(R.id.action_splashFragment_to_userProfileFragment2, null,
new NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build())
The sample solution is add a onBackPressedDispatcher on Owner Activity of fragment/navigation:
https://developer.android.com/guide/navigation/navigation-custom-back#implement_custom_back_navigation

Categories

Resources