Communication of Fragments in the Fragment, using Navigation Component, MVVM and Koin

Piotr Prus
Apr 22, 2020 · 4 min read
Image for post
Image for post
Photo by Jordan Madrid on Unsplash

Good in-app navigation is essential for the user experience. As is the flexibility that allows you to program to the new requirements. By introducing Navigation Component in Android Jetpack, Google showed a straight way to maintain the flow of our app from a central location.

In this article, I will show you how to use nested fragments in Navigation with additional help from Koin to provide the same instance of ViewModel across different destinations.

The setup

We are going to follow the single activity navigation pattern recommended by Google (link). For this activity, let’s set 3 different fragments and name them One, Two and Three.

Fragment Three will hold additionally 2 other fragments named X and Y.
Just like on the sketch below:

Image for post
Image for post

Navigation Host

Navigation used in the navigation component is done with Nav Host Fragment.

NavHostFragment provides an area within your layout for self-contained navigation to occur.

This means the above schema is not entirely correct. We are missing very important pieces, which are NavHostFragments. NavHost can be attached to Activity or Fragment and since it is a Fragment container, it has its lifecycle.

More about that in the next part of the article. Let’s see the updated schema for our navigation:

Image for post
Image for post

Navigation Host Scope

As mentioned earlier, the NavHost is a fragment container and hence, it has its lifecycle. The creation of NavHostFragment requires a view, so the lifecycle functions where NavHostFragment can be created or called are as follows:

  • onCreate() for an Activity
  • onActivityCreated() for Fragments

Let’s have a look at some code.

Activity layout file with navHostFragment attached

We do not need to specify any additional information in Activity class since this NavHostFragment(with associated NavController) is the default one, whenever we want to call it, we can use: findNavController(). The Navigation Component Library will check if any nav controller is associated with this view. If there is no NavHostFragment to which we could refer, the IllegalStateException will be thrown.

The creation looks a bit different for a fragment that is already a part of Navigation within the default(parent) NavController. We need to specify explicitly which fragment will be used as HostFragment. Apart from that, we also need to add the <fragment/> to the view. This time, we set the variable: defaultNavHost to false.

As you can see on the above snippet, to find the proper fragment, we are using childFragmentManager.

Now, whenever we would like to use it in fragments X or Y, we will just call a findNavController(). This will return navController associated with nested NavHostFragment(created in fragment Three). It works, because fragments X and Y are created within Fragment Three. Those, cannot refer to the main navController, cause it operates in different scope. Ok, so how to use it? Let me explain in the next paragraph.

Beyond the Scope

We have already determined that the nested fragment(child) cannot directly refer to the activity navHostFragment. Which Fragment can? All the Fragments that are included in the main navigation graph(navHostFragment). To avoid confusion I marked those with a red rectangle.

Image for post
Image for post

We are interested in fragment Three, which is part of the main navigation and a starter point of nested navigation. We will call requireParentFragment from the fragment X to access fragment Three. RequireParentFragment returns the parent Fragment containing this Fragment.

val fragmentThree = requireParentFragment().requireParentFragment() as ThreeFragment

As you can see, we are calling it twice, because NavHostFragment is a fragment too. This is essential for providing one ViewModel for all child fragments that are in the scope of parent fragment as well.

One (ViewModel) to rule them all

Consider the following use case: two child fragments (as shown in the example above) communicate with each other to display some data and a parent fragment that manages their visibility based on this data. Using Google's recommended architecture, we can use ViewModel for app logic. This raises a problem of the definition of the scope for that ViewModel.

At first, I thought it would not be difficult. All you have to do is initialize the ViewModel in the scope of NavHost Fragment. Unfortunately, this is not the case. To share an instance of ViewModel for parent and child fragments, we need to set the scope to parent fragment (Three), not NavHost, cause Three is a parent for the NavHost. How to do it?

  • Using ViewModelProvider

Disclaimer: ViewModelProviders is currently deprecated, as for fragment:2.2.0

We can use the constructor for ViewModelProvider() and pass ViewModelStore as a parameter. ViewModelStore is an object that manages the current state of ViewModel. More about it, here.

  • Using by viewmodels()

Property delegate from the library: androidx.fragment:fragment-ktx:1.2.4. It creates ViewModel lazy and scopes it to this Fragment by default. The default scope may be overridden with parameter: ownerProducer.

val viewModel by viewModels<ParentViewModel>(ownerProducer = { requireParentFragment().requireParentFragment() })

This is also a property delegate that provides the ViewModel lazily. The default scope may be overridden with parameter: from.

val viewModel: ParentViewModel by sharedViewModel(from = { requireParentFragment().requireParentFragment() })

That’s all. If you want to use the same instance of viewModel in a few fragments, define the scope properly and you are ready to go.

Schibsted Tech Polska

High quality software development for a family of digital…

Piotr Prus

Written by

Android Developer @Schibsted Enthusiast of kotlin and clean architecture.

Schibsted Tech Polska

High quality software development for a family of digital consumer brands across media, online marketplaces and technology ventures.

Piotr Prus

Written by

Android Developer @Schibsted Enthusiast of kotlin and clean architecture.

Schibsted Tech Polska

High quality software development for a family of digital consumer brands across media, online marketplaces and technology ventures.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store