Screen 1: +---------------------+ | Welcome Screen | | | | [Go to Details] | +---------------------+ Screen 2: +---------------------+ | Details Screen | | | | [Back to Welcome] | +---------------------+
NavHost and NavController in Android Kotlin - Mini App: Build & Ship
package com.example.navigationdemo import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.navigation.fragment.NavHostFragment class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // TODO: Setup NavHostFragment and NavController } } // TODO: Create two fragments: WelcomeFragment and DetailsFragment // Each fragment should have a button to navigate
package com.example.navigationdemo import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.navigation.fragment.NavHostFragment import androidx.navigation.NavController class MainActivity : AppCompatActivity() { private lateinit var navController: NavController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment) as NavHostFragment navController = navHostFragment.navController } } // WelcomeFragment.kt package com.example.navigationdemo import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import android.widget.Button class WelcomeFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_welcome, container, false) val button = view.findViewById<Button>(R.id.button_go_to_details) button.setOnClickListener { findNavController().navigate(R.id.action_welcomeFragment_to_detailsFragment) } return view } } // DetailsFragment.kt package com.example.navigationdemo import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import android.widget.Button class DetailsFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_details, container, false) val button = view.findViewById<Button>(R.id.button_back_to_welcome) button.setOnClickListener { findNavController().navigate(R.id.action_detailsFragment_to_welcomeFragment) } return view } } // activity_main.xml <?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"> <fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:navGraph="@navigation/nav_graph" app:defaultNavHost="true" /> </androidx.constraintlayout.widget.ConstraintLayout> // fragment_welcome.xml <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <Button android:id="@+id/button_go_to_details" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Go to Details" android:layout_gravity="center" /> </FrameLayout> // fragment_details.xml <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <Button android:id="@+id/button_back_to_welcome" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Back to Welcome" android:layout_gravity="center" /> </FrameLayout> // nav_graph.xml (in res/navigation) <?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_graph" app:startDestination="@id/welcomeFragment"> <fragment android:id="@+id/welcomeFragment" android:name="com.example.navigationdemo.WelcomeFragment" android:label="Welcome"> <action android:id="@+id/action_welcomeFragment_to_detailsFragment" app:destination="@id/detailsFragment" /> </fragment> <fragment android:id="@+id/detailsFragment" android:name="com.example.navigationdemo.DetailsFragment" android:label="Details"> <action android:id="@+id/action_detailsFragment_to_welcomeFragment" app:destination="@id/welcomeFragment" /> </fragment> </navigation>
This solution uses Android's Navigation component to manage navigation between two fragments.
The NavHostFragment is placed in the activity's layout to host the navigation graph. The NavController is obtained from the NavHostFragment in MainActivity.
Two fragments are created: WelcomeFragment and DetailsFragment. Each fragment has a button that triggers navigation actions defined in the navigation graph XML (nav_graph.xml).
When the user taps the button in WelcomeFragment, the app navigates to DetailsFragment. The button in DetailsFragment navigates back to WelcomeFragment.
This setup follows Android best practices for navigation, keeping UI and navigation logic clean and separate.
Screen 1: +---------------------+ | Welcome Screen | | | | [Go to Details] | +---------------------+ Screen 2: +---------------------+ | Details Screen | | | | [Back to Welcome] | +---------------------+
Practice
NavHost in Android navigation?Solution
Step 1: Understand NavHost purpose
NavHost is a container that shows the current screen (fragment) in your app.Step 2: Compare options
Only It displays the current screen inside a container. correctly describes NavHost's role as a screen container.Final Answer:
It displays the current screen inside a container. -> Option AQuick Check:
NavHost = Screen container [OK]
- Confusing NavHost with NavController
- Thinking NavHost manages data or services
- Assuming NavHost handles navigation logic
NavController inside a Fragment?Solution
Step 1: Recall NavController retrieval method
In a Fragment, the standard way is to callfindNavController()extension function.Step 2: Check other options
The other options are invalid or do not exist in Android navigation API.Final Answer:
val navController = findNavController() -> Option CQuick Check:
Use findNavController() in Fragment [OK]
- Trying to instantiate NavController directly
- Using wrong method names like getNavHost()
- Confusing NavHost with NavController
findNavController().navigate(R.id.action_home_to_detail)
What happens when this code runs?
Solution
Step 1: Understand navigate() usage
Thenavigate()method moves the app to the destination defined by the action ID.Step 2: Interpret the action ID
R.id.action_home_to_detailis an action from Home to Detail screen in navigation graph.Final Answer:
The app navigates from Home screen to Detail screen. -> Option BQuick Check:
navigate(actionId) moves between screens [OK]
- Thinking navigate() closes or restarts screens
- Assuming navigate() requires a string route
- Confusing action IDs with fragment IDs
val navController = NavController() navController.navigate(R.id.action_main_to_settings)
Solution
Step 1: Check NavController instantiation
NavController is obtained viafindNavController(), not by calling constructor.Step 2: Validate navigate() usage
Using an action ID with navigate() is correct; no string needed.Final Answer:
NavController cannot be instantiated directly with constructor. -> Option DQuick Check:
NavController is retrieved, not created [OK]
- Trying to create NavController with new keyword or constructor
- Confusing action IDs with string routes
- Misunderstanding lateinit usage
Solution
Step 1: Understand safe args usage
Using generated directions class with arguments is the recommended way to pass data safely.Step 2: Compare other options
Using Intent bypasses the Navigation Component. Passing a bundle without proper key-value pair syntax is invalid. Instantiating NavController directly fails.Final Answer:
val action = ScreenADirections.actionScreenAToScreenB(userId) findNavController().navigate(action) -> Option AQuick Check:
Safe args = directions class + navigate(action) [OK]
- Instantiating NavController manually
- Passing arguments without safe args
- Using Intent instead of NavController for navigation
Ready to prove it?
Pass the test and this pattern moves unknown
