How to go from one fragment to another fragment in kotlin? - android

I have a button in fragment A and when I click on fragment A, I want it to be redirected to fragment B. How to achieve this in kotlin? Right now I am using this code but app crash.
btntest.setOnClickListener {
var intent = Intent(view.context, FragmentB::class.java)
startActivity(intent)
}

I'd suggest you take a look on the navigation component. First, you create a Navigation resource file (usually referred to as Nav Graph), where you add the fragments you wish to connect.
Make sure to give an ID to each fragment (that's what you'll use to navigate between them later). Additionally, you can use app:startDestination in order to set the first fragment to be shown.
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
app:startDestination="#id/blankFragment">
<fragment
android:id="#+id/blankFragment"
android:name="com.example.cashdog.cashdog.BlankFragment"
android:label="#string/label_blank"
tools:layout="#layout/fragment_blank" />
</navigation>
Then, in the activity/fragment that will be hosting the fragments, add a FragmentContainerView and set the graph to the one you created.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
Finally, in order to navigate between fragments, use this in the Activity that is hosting the navigation (use getActivity() if a Fragment is hosting the navigation).
// as per defined in your FragmentContainerView
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
// Navigate using the IDs you defined in your Nav Graph
navController.navigate(R.id.blankFragment)
Source: https://developer.android.com/guide/navigation/navigation-getting-started
Edit: if you want to access the controller within your Fragment, use activity?.supportFragmentManager to get the fragment manager.

Try this in From Fragment, after you have set action in nav graph xml.
findNavController().navigate(R.id.action_from_fragmentA_to_fragmentB)

set in adapter click listner
loadFragment(SettingFragment())
private fun loadFragment(homeFragment: SettingFragment) {
val transaction = (context as AppCompatActivity).supportFragmentManager.beginTransaction()
transaction.replace(R.id.container,homeFragment)
transaction.addToBackStack(null)
transaction.commit()
}

Related

Navigating back in a multi-module Android application

I'm currently working on a multi-module application which is structured as below and I can't get the back navigation to function correctly when navigating between feature modules. Either nothing happens or the app closes instead of just going back to the previous destination.
- app
- feature-explore
- feature-search
- feature-detail
Each of the feature modules has it's own navigation graph which is brought together in the main navigation graph located in the app module. The app module contains a single MainActivity with a FragmentContainerView and BottomNavigationView in its layout...
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/main_bottomnav"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/menu_bottomnav" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/main_navhost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/nav_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
The BottomNavigationView is linked to a menu to hook up feature-explore and feature-search modules as main destinations with their own navigation graphs...
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)
navController = (supportFragmentManager.findFragmentById(R.id.main_navhost) as NavHostFragment).navController
binding.mainBottomnav.setupWithNavController(
navController
)
}
}
I've then set up a global action in the main navigation graph to allow each of the feature-explore and feature-search modules to navigate to the feature-detail module (e.g. clicking an item in a list opens up the detail of that item via a separate feature module).
This is the main navigation graph...
<?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_main"
app:startDestination="#id/nav_explore">
<include app:graph="#navigation/nav_detail" />
<include app:graph="#navigation/nav_explore" />
<include app:graph="#navigation/nav_search" />
<action
android:id="#+id/action_global_nav_detail"
app:destination="#id/nav_detail">
<argument
android:name="id"
app:argType="long" />
</action>
</navigation>
Switching between menu items works fine as does navigating using the global action above to move to the nav_detail graph.
From the fragment in that nav_detail graph though I would expect the device back button to take the user back to the previous view in the back stack. Instead nothing happens and the user stays on the detail fragment (the start destination of nav_detail).
If I update the global action to specify a popUpTo value then MainActivity is closed altogether...
<action
android:id="#+id/action_global_nav_detail"
app:destination="#id/nav_detail"
app:popUpTo="#id/nav_explore"
app:popUpToInclusive="false">
<argument
android:name="id"
app:argType="long" />
</action>
Anyone have any suggestions on how the get the back navigation from nav_detail to behave as expected?
Note - I'm using Navigation Components v2.5.3.
After further investigation I found that the issue wasn't actually to do with the navigation itself. The back press was actually going back to the previous fragment.
I was however using LiveData to hold an ID of the selected item and, as the view model and the LiveData value was retained, it was observed again immediately after navigating back and therefore navigated to the detail fragment again.
See similar issue here with a link to options for changing approach with the LiveData to avoid this problem.

BottomNavigationView with nested graph

I have a problem in my application when using nested graph in my nave graph xml file
It's my nav_graph file
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#navigation/nav_entries">
<include app:graph="#navigation/nav_entries"/>
<include app:graph="#navigation/nav_states"/>
<include app:graph="#navigation/nav_calendar"/>
<include app:graph="#navigation/nav_more"/>
</navigation>
and this is fragment container view in activity_main
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
And this is code from main activity to setup bottom navigation view with nave controller
private fun setupUi() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment
navController = navHostFragment.navController()
binding.bottomNavigationView.setupWithNavController(navController)
}
But when I run the app, It will crash and this is error :
navigation destination com.iranmobiledev.moodino:navigation/nav_entries is not a direct child of this NavGraph
Help me to fix.
It's incorrect.
In navigation, not all nested navigation graphs should be defined with include, and a node(fragment) should be defined as the start destination. You can define other fragments in the nested navigation graph and use it with the include tag.
Use this code:
app:startDestination="#id/first_fragment_id">
Instead of:
app:startDestination="#navigation/nav_entries">
I faced the same issue.
instead of adding the graph in xml, inflate it programmatically.
the issue will be resolved.

Single Activity login flow Android

I am currently working on a school project that displays your timetable, grades, subjects and so on. To use any functionality in the Android application you have to be logged in. Here comes my problem:
When the users starts the application for the first time, they should see a login fragment. Once the login is completed they will be presented with a setup screen where they can choose color themes for grades and other user specific things. Only then should they be presented with the actual main fragment. On the second start the user should directly see the main fragment
The main fragment is a FragmentContainerView and a BottomNavigationBar that hosts 5 other fragments. In each of those subfragments you can click on items. Then a different fragment should be presented that shows some more details. These fragments however should overlap the bottom navigation so that you have to navigate back before you can choose a different fragment in the bottom navigation bar.
As far as I can tell I need nested FragmentContainerViews. The MainActivity should be
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The MainActivity should host the LoginFragment, SetupFragment and a MainFragment. In the MainFragment should be like this
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#+id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
I would like to achieve all of this with a navigation graph. Do I have to use one graph or two for each FragmentContainerView? Then there is also the problem with the detail Fragments. If for example I have a HomeFragment as a child of the MainFragment which is a child of the MainActivity and in the HomeFragment the user clicks on for example a button to the SettingsFragment, this fragment should be displayed as a child of the MainActivity. How would I get this working? I've already had a look at this question but don't really understand how to implement it.
How to setup Multiple nested FragmentContainerViews with respective navigation graphs?
Could somebody maybe create a very simple implementation of this. I especially need help with the nested FragmentContainerViews, the BottomNavigationBar and the NavigationGraph. I've also come across the problem that the bottom navigation bar doesn't respond anymore.
Thanks for your help in advance. Please let me know if you need any more details.
1.way
val homeFragment = HomeFragment()
val listFragment = ListFragment()
val profileFragment = ProfileFragment()
nav_view.setOnItemSelectedListener {
when (it) {
R.id.homeFragment -> setCurrentFragment(homeFragment)
R.id.listFragment -> setCurrentFragment(listFragment)
R.id.profileFragment ->
setCurrentFragment(profileFragment)
}
return#setOnItemSelectedListener
}
private fun setCurrentFragment(fragment: Fragment) =
supportFragmentManager.beginTransaction().apply {
replace(R.id.nav_host_fragment_activity_main, fragment)
addToBackStack(null) //If you delete this, press the back button, it will not return to the previous fragment.
commit()
}
2.way
Navigation with
https://www.youtube.com/watch?v=DI0NIk-7cz8&t=527s&ab_channel=Stevdza-San

Android- replacing a fragment inside fragment container (containing multiple fragments) replaces the whole container rather than that one fragment

I have a fragment container, which contains multiple other fragments, but when I am trying to replace one of the fragments inside the fragment container(to reload that fragment), the fragment does get replaced but instead of only that particular fragment getting replaced the whole container is getting replaced by that fragment and even when navigating to other fragments only that fragment is showing up.
I only want to replace one particular fragment present in the container not the whole container.
The fragment I want to replace is TroubleshootFragment. The code I am using to replace the fragment is :
val newfrag = TroubleshootFragment.newInstance()
val ftr = (this.activity)?.supportFragmentManager?.beginTransaction()
?.replace(R.id.container, newfrag)?.commit()
The xml file for container is :
<RelativeLayout 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/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/background_image"
tools:context=".view.HomeActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/bottom_navigation"
android:layout_below="#id/logoImageView"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_main" />
The xml file for the fragment is :
<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_main.xml"
app:startDestination="#id/aboutFragment">
<fragment
android:id="#+id/troubleshootFragment"
android:name="com.p.b.view.fragment.TroubleshootFragment"
android:label="Troubleshoot"
tools:layout="#layout/troubleshoot_layout">
<action
android:id="#+id/action_troubleshootSuccessFragment"
app:destination="#id/troubleshootSuccessFragment" />
</fragment>
I have tried the following code as well :
val ftr = (this.activity)?.supportFragmentManager?.beginTransaction()
?.replace(R.id.troubleshootFragment, newfrag)?.commit()
but this causes the app to crash, then how should I replace the fragment so that only Troubleshoot Fragment gets replaced and not the whole container containing other multiple fragments?
Edit : The log shows this :
I/FragmentNavigator: Ignoring navigate() call: FragmentManager has already saved its state when trying to navigate to other fragments.

State of nested Fragments is lost when returned to same tab in 'Navigation Architecture Component'

I'm exploring the the 'Navigation Architecture Component' concept which introduced in Google I/O 2018 last month.
Let say I have an activity with a bottom navigation view and a 'fragment' to host all fragments:-
<android.support.constraint.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.MainActivity">
<fragment
android:id="#+id/root_form"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="#navigation/nav_graph"
app:defaultNavHost="true"
/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/navigation" />
</android.support.constraint.ConstraintLayout>
And I have 2 actions/tabs in my bottom navigation view which will show FragmentA and FragmentB respectively. FragmentB have a nested (child) fragment FragmentC which can be open from FragmentB.
in my Activity class I link the bottom navigation view with the navigation controller using the 'setupWithNavController()' function.
(Kotlin extension function that included in ' androidx.navigation.ui' package) :-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bottom_navigation.setupWithNavController(findNavController(R.id.root_form))
}
Everything works fine as expected.
But incase user navigate to FragmentA while he is in FragmentC and come back agin to same tab then he will only get the FragmentB not the fragmentC. Its a common pattern in bottom navigation when user return to same tab it should show the last child/nested fragment he was viewing.
I know we can handle this if we have our own fragment transaction but How to resolve this issue in 'Navigation Architecture Component' domain?
You can use a container fragment which holds the viewpager and is the navigation location from parent. When you your in A or B you can hit C and then when you go back it will go back to parent which will still be at which ever fragment tab you were at.
The nav graph never knows about Frag A or B but it knows about the container.
You can slove this issue by updateing the action inside navGraph file.
Just specify popUpTo to the parent fragment (FragmentB) and also set popUpToInclusive to true
<fragment
android:id="#+id/FragmentB"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/root_form"
app:destination="#id/FragmentC"
app:popUpTo="#id/FragmentB"
app:popUpToInclusive="true" />
</fragment>

Categories

Resources