Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm having an issue with a very simple task with Navigation component. I have just 2 screens: MainFragment and SearchFragment.

When I try to go from MainFragment to SearchFragment by navigation with an action it works perfectly. Then I press the back button and naturally it goes back to the MainFragment.

The issue is, when I click the same button the second time to go again to the SearchFragment, I receive the following error:

java.lang.IllegalArgumentException: Navigation action/destination action_mainFragment_to_searchFragment cannot be found from the current destination Destination(searchFragment)

I'm navigating from the MainFragment to Search like this:

findNavController().navigate(MainFragmentDirections.actionMainFragmentToSearchFragment())

I tried redoing the nav_graph.xml but without success.

If I just navigate with the id directly, it works fine and I go back and forth as many times as I want

findNavController().navigate(R.id.searchFragment)

Any ideas how to fix the issue with the safe args?

Edit:

This is my nav_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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph_main"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="news.presentation.main.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main">
        <action
            android:id="@+id/action_mainFragment_to_searchFragment"
            app:destination="@id/searchFragment" />
    </fragment>
    <fragment
        android:id="@+id/searchFragment"
        android:name="news.presentation.search.SearchFragment"
        android:label="fragment_search"
        tools:layout="@layout/fragment_search" />
</navigation>

This is the activity (it's basically just a container for the fragments):

This is the activity_home.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView 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/homeFragContainer"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph_main"
    tools:context=".presentation.HomeActivity" />

And this is the HomeActivity.kt

@AndroidEntryPoint
class HomeActivity : AppCompatActivity(R.layout.activity_home)

This is the HomeFragment:

@AndroidEntryPoint
class MainFragment : Fragment(R.layout.fragment_main) {

    private val binding by viewBinding(FragmentMainBinding::bind)
    private val viewModel by viewModels<MainViewModel>()

    private val articlesAdapter = ArticlesAdapter(::onSubscriptionClicked)
    private lateinit var layoutManager: LinearLayoutManager

    override fun onViewCreated(view: View, bundle: Bundle?) {
        super.onViewCreated(view, bundle)
        setupViews()
        setupViewModel()
        setupRecyclerView()
    }

    private fun setupViews() {
        binding.toolbar.title = getString(R.string.app_name)
        binding.fab.setOnClickListener {
            viewModel.intent.offer(MainViewModel.Intent.SearchClicked)
        }
    }

    private fun setupViewModel() {
        viewModel.state
            .onEach(::handleState)
            .launchIn(lifecycleScope)
        viewModel.feedFlow
            .onEach(articlesAdapter::submitList)
            .launchIn(lifecycleScope)
    }

    private fun setupRecyclerView() {
        layoutManager = LinearLayoutManager(requireContext())
        binding.recycler.layoutManager = layoutManager
        binding.recycler.adapter = articlesAdapter
        binding.recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                val canScroll = recyclerView.canScrollVertically(-1)
                binding.toolbar.isLifted = canScroll
            }
        })
    }

    private fun onSubscriptionClicked(article: Article) {
        viewModel.intent.offer(MainViewModel.Intent.ItemClicked(article))
    }

    private fun handleState(state: MainViewModel.State) = when (state) {
        NavigateToSearch -> findNavController().navigate(MainFragmentDirections.actionMainFragmentToSearchFragment())
        is FeedReady -> binding.progress.isVisible = false
        Loading -> binding.progress.isVisible = true
        is NavigateToArticle -> {
            // works
            // findNavController().navigate(
            //     R.id.articleFragment,
            //     bundleOf("articleLink" to state.link)
            // )
        }
    }
}

And this is the XML for it:

<?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"
    android:clipChildren="false"
    android:clipToPadding="false"
    tools:context=".presentation.main.MainFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottomNav"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        tools:listitem="@layout/item_article_big" />

    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/standard"
        android:text="Add Feed"
        android:textAlignment="center"
        app:icon="@drawable/ic_add"
        app:layout_constraintBottom_toTopOf="@id/bottomNav"
        app:layout_constraintEnd_toEndOf="parent" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:menu="@menu/menu_main" />

    <tgo1014.news.presentation.customview.LiftableToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clipChildren="false"
        android:clipToPadding="false"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.progressindicator.LinearProgressIndicator
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        app:layout_constraintBottom_toBottomOf="@id/toolbar"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        tools:visibility="visible" />

</androidx.constraintlayout.widget.ConstraintLayout>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
270 views
Welcome To Ask or Share your Answers For Others

1 Answer

the error is due to the lifecycle. Replace this:

viewModel.state
    .onEach(::handleState)
    .launchIn(lifecycleScope)

by

viewModel.state
    .onEach(::handleState)
    .launchIn(viewLifecycleOwner.lifecycleScope)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...