Skip to Content
Course content

187: Navigation Component with Kotlin

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've been working on this movie discovery app, and right now, I'm stuck on a problem we've all faced: moving from a list of movies to a specific movie's detail page. In the past, I would have just written a bunch of FragmentTransaction code, but honestly? I'm tired of managing the back stack manually. It's a recipe for memory leaks and "Why did the app just close when I hit back?" bugs.

The Manual Fragment Headache

Just for a second, look at how I was doing this before I decided to use the Navigation Component. I had this block of code inside my MovieListFragment:

val detailFragment = MovieDetailFragment()
parentFragmentManager.beginTransaction()
    .replace(R.id.fragment_container, detailFragment)
    .addToBackStack(null)
    .commit()

It looks simple enough for one screen, but as soon as I add a "Reviews" screen, a "Cast" screen, and a "Settings" page, my Activity becomes a giant traffic cop. I'm spending more time managing transactions than actually writing features. There's no single place to look at the app's flow; it's all buried in Kotlin files. I need a map.

Drawing the Map

So, I've added the Navigation Component. Instead of coding the transitions, I created a nav_graph.xml file. I dragged in two fragments: MovieListFragment and MovieDetailFragment. Then, I drew an arrow—an "Action"—from the list to the detail.

Now, the "map" knows that action_movieList_to_movieDetail is the valid path. I don't have to tell the FragmentManager to replace the container anymore. I just have to tell the NavController to follow that specific arrow. Here is how I'm triggering it now:

movieAdapter.onMovieClicked = { movie ->
    findNavController().navigate(R.id.action_movieList_to_movieDetail)
}

That's much cleaner. The NavController handles the transaction, the back stack, and the animation. But wait—I've just realized a problem. The detail screen is loading, but it's empty. It doesn't know which movie I clicked on.

Avoiding the Bundle Trap

My first instinct was to use a Bundle. I've done it a thousand times. I'd put the movie ID in a bundle, pass it along, and then call arguments?.getString("movie_id") in the destination. But I hate that. If I typo "movie_id" as "mvoie_id" in one of those two files, the app crashes or shows a blank screen, and the compiler won't say a word to me.

I decided to use Safe Args. It's a Gradle plugin that generates "Directions" classes. Instead of using a raw ID for the action, it creates a class specifically for the MovieListFragment. Let's see how this changes the code:

// No more R.id.action...
val action = MovieListFragmentDirections.actionMovieListToMovieDetail(movieId = movie.id)
findNavController().navigate(action)

This is a game changer. If I change the argument type in the XML from a String to an Int, this Kotlin code will suddenly turn red. The compiler is now helping me manage my navigation logic. I'm not guessing what the destination expects anymore; the Directions class tells me exactly what's required.

Picking Up the Pieces

Now I just need to retrieve that ID on the other side. In MovieDetailFragment, I could use arguments, but since I'm using Safe Args, there's a delegated property that makes this feel like native Kotlin:

val args: MovieDetailFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val movieId = args.movieId
    // Now I can fetch the movie details using this ID
}

The transition is seamless. I've gone from manually swapping fragments and crossing my fingers that the keys match, to a type-safe system where the entire flow of the app is visualized in one XML file and enforced by the compiler.




📋 Practical Task

Build a Type-Safe Movie Genre Navigator

You are tasked with adding a "Genre" navigation flow to the existing movie app. Follow these requirements:

  • Create a new fragment called GenreListFragment and another called GenreDetailFragment.
  • In the nav_graph.xml, create a navigation action from GenreListFragment to GenreDetailFragment.
  • Define an argument in the GenreDetailFragment destination called genreName (Type: String).
  • Implement the click listener in GenreListFragment using the generated Directions class to pass the selected genre's name to the detail fragment.
  • In GenreDetailFragment, use the by navArgs() delegate to retrieve the genreName and display it in a TextView.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.