In my application I used single activity and use some fragments!
I want first of all show SplashFragment and checked user token, if exists open HomeFragment else open RegisterFragment!
I write below codes, but after register user show me below error!
Logcat error :
java.lang.IllegalArgumentException: Navigation action/destination my.app:id/actionSplashToHome cannot be found from the current destination Destination(my.app:id/registerFragment) label=fragment_register class=my.app.ui.register.RegisterFragment
at androidx.navigation.NavController.navigate(NavController.kt:1536)
at androidx.navigation.NavController.navigate(NavController.kt:1468)
at androidx.navigation.NavController.navigate(NavController.kt:1450)
at androidx.navigation.NavController.navigate(NavController.kt:1433)
at my.app.ui.splash.SplashFragment$onViewCreated$1$1.emit(SplashFragment.kt:43)
at my.app.ui.splash.SplashFragment$onViewCreated$1$1.emit(SplashFragment.kt:38)
at my.app.utils.UserInfo$getUserToken$$inlined$map$1$2.emit(Emitters.kt:224)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:77)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:59)
at androidx.datastore.core.SingleProcessDataStore$data$1$invokeSuspend$$inlined$map$1$2.emit(Collect.kt:137)
at kotlinx.coroutines.flow.FlowKt__LimitKt$dropWhile$1$1.emit(Limit.kt:37)
at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:398)
at kotlinx.coroutines.flow.StateFlowImpl$collect$1.invokeSuspend(Unknown Source:15)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
RegisterFragment codes:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//InitViews
binding.apply {
//Submit
submitBtn.setOnClickListener { it ->
val name = nameEdt.text.toString()
val email = emailEdt.text.toString()
val password = passwordEdt.text.toString()
//Validation
if (name.isNotEmpty() || email.isNotEmpty() || password.isNotEmpty()) {
body.name = name
body.email = email
body.password = password
viewModel.registerUser(body)
viewModel.registerUser.observe(viewLifecycleOwner) { itResponse ->
Log.e("UserInfoLog","1 : " itResponse.email.toString())
lifecycle.coroutineScope.launchWhenCreated {
userDataStore.saveUserToken(itResponse.email.toString())
Log.e("UserInfoLog","2 : " itResponse.email.toString())
findNavController().navigateUp()
}
}
} else {
Snackbar.make(it, getString(R.string.fillAllFields), Snackbar.LENGTH_SHORT).show()
}
SplashFragment codes:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Check user token
lifecycle.coroutineScope.launchWhenCreated {
delay(2000)
userDataStore.getUserToken().collect {
if (it.isEmpty()) {
findNavController().navigate(R.id.actionSplashToRegister)
} else {
findNavController().navigate(R.id.actionSplashToHome)
}
}
}
}
Navigation's codes:
<?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_main"
app:startDestination="@id/splashFragment">
<fragment
android:id="@ id/splashFragment"
android:name="my.app.ui.splash.SplashFragment"
android:label="fragment_splash"
tools:layout="@layout/fragment_splash">
<action
android:id="@ id/actionSplashToRegister"
app:destination="@id/registerFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@ id/actionSplashToHome"
app:destination="@id/homeFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
</fragment>
<fragment
android:id="@ id/registerFragment"
android:name="my.app.ui.register.RegisterFragment"
android:label="fragment_register"
tools:layout="@layout/fragment_register">
<action
android:id="@ id/actionRegisterToHome"
app:destination="@id/homeFragment" />
</fragment>
<fragment
android:id="@ id/homeFragment"
android:name="my.app.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@ id/action_homeFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@ id/favoriteFragment"
android:name="my.app.ui.favorite.FavoriteFragment"
android:label="fragment_favorite"
tools:layout="@layout/fragment_favorite" />
<fragment
android:id="@ id/searchFragment"
android:name="my.app.ui.search.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@ id/detailFragment"
android:name="my.app.ui.detail.DetailFragment"
android:label="fragment_detail"
tools:layout="@layout/fragment_detail" />
</navigation>
How can I fix it?
CodePudding user response:
Looks like findController()
is still using the destination of RegisterFragment
for some reason.
My guess the root cause is because the thread confuses findController()
which makes it still think you are at RegisterFragment
.
As a workaround fix before digging too deep into thread management, you can try to make action actionSplashToHome
to a Global Action
in your navigation layout file. Just move that action outside the fragment
tag, then any fragment in this navigation file can use this action.
<?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_main"
app:startDestination="@id/splashFragment">
<fragment
android:id="@ id/splashFragment"
android:name="my.app.ui.splash.SplashFragment"
android:label="fragment_splash"
tools:layout="@layout/fragment_splash">
<action
android:id="@ id/actionSplashToRegister"
app:destination="@id/registerFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
<action
android:id="@ id/action_global_splash_ToHome"
app:destination="@id/homeFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
<fragment
android:id="@ id/registerFragment"
android:name="my.app.ui.register.RegisterFragment"
android:label="fragment_register"
tools:layout="@layout/fragment_register">
<action
android:id="@ id/actionRegisterToHome"
app:destination="@id/homeFragment" />
</fragment>
<fragment
android:id="@ id/homeFragment"
android:name="my.app.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@ id/action_homeFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@ id/favoriteFragment"
android:name="my.app.ui.favorite.FavoriteFragment"
android:label="fragment_favorite"
tools:layout="@layout/fragment_favorite" />
<fragment
android:id="@ id/searchFragment"
android:name="my.app.ui.search.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@ id/detailFragment"
android:name="my.app.ui.detail.DetailFragment"
android:label="fragment_detail"
tools:layout="@layout/fragment_detail" />
</navigation>
No need to change your code in Splashfragment, still use findNavController().navigate(R.id.action_global_splash_ToHome)