📋 Screens, Lists और Navigation · Lesson 25
Navigation Component और Safe Args
Navigation Component क्यों?
Manual FragmentManager
- हर जगह replace() + addToBackStack
- Back stack bugs
- Toolbar title हाथ से
- String keys वाले Bundles
Navigation Component
- सारी screens एक graph में
- Back और Up handled
- Toolbar, drawer, bottom nav अपने आप जुड़े
- Type-safe arguments (Safe Args)
Dependencies
TOML + Kotlin DSL
# gradle/libs.versions.toml
navigation = "2.8.5"
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation" }
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation" }
// app/build.gradle.kts
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)Navigation graph
res पर right-click → New → Android Resource File → type Navigation → nav_graph। Design view screens को boxes और actions को arrows की तरह दिखाता है। यह Student Diary का असली graph है:
XML – res/navigation/nav_graph.xml
<?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/notesFragment">
<!-- Open the editor from anywhere (FAB, list item) -->
<action
android:id="@+id/action_global_editor"
app:destination="@id/editorFragment"
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/notesFragment"
android:name="com.codingeasily.studentdiary.ui.notes.NotesFragment"
android:label="@string/app_name" />
<!-- The same list fragment, reused with different arguments -->
<fragment
android:id="@+id/favoritesFragment"
android:name="com.codingeasily.studentdiary.ui.notes.NoteListFragment"
android:label="@string/menu_favourites">
<argument
android:name="filter"
android:defaultValue="FAVORITES"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/subjectsFragment"
android:name="com.codingeasily.studentdiary.ui.subjects.SubjectsFragment"
android:label="@string/menu_subjects">
<action
android:id="@+id/action_subjects_to_subjectNotes"
app:destination="@id/subjectNotesFragment" />
</fragment>
<fragment
android:id="@+id/subjectNotesFragment"
android:name="com.codingeasily.studentdiary.ui.notes.NoteListFragment"
android:label="{subject}">
<argument
android:name="filter"
android:defaultValue="SUBJECT"
app:argType="string" />
<argument
android:name="subject"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/statsFragment"
android:name="com.codingeasily.studentdiary.ui.stats.StatsFragment"
android:label="@string/menu_stats" />
<fragment
android:id="@+id/settingsFragment"
android:name="com.codingeasily.studentdiary.ui.settings.SettingsFragment"
android:label="@string/menu_settings" />
<fragment
android:id="@+id/helpFragment"
android:name="com.codingeasily.studentdiary.ui.help.HelpFragment"
android:label="@string/menu_help" />
<fragment
android:id="@+id/editorFragment"
android:name="com.codingeasily.studentdiary.ui.editor.AddEditNoteFragment"
android:label="@string/note">
<argument
android:name="noteId"
android:defaultValue="-1L"
app:argType="long" />
</fragment>
</navigation>NavHostFragment
XML
<!-- activity_main.xml (Student Diary, part) -->
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />app:defaultNavHost="true" system Back button को graph के अंदर पीछे जाने देता है।
Actions से navigate
Kotlin
// Student Diary – NoteListFragment: open the editor for a note
findNavController().navigate(R.id.action_global_editor, bundleOf("noteId" to note.id))
// MainActivity – FAB opens an empty editor (noteId keeps its default -1L)
binding.fab.setOnClickListener { navController.navigate(R.id.action_global_editor) }
// SubjectsFragment – open notes of one subject
findNavController().navigate(
R.id.action_subjects_to_subjectNotes,
bundleOf("filter" to "SUBJECT", "subject" to item.subject)
)
// Go back
findNavController().navigateUp()Arguments (Bundle तरीका)
Kotlin
// AddEditNoteFragment
val noteId = arguments?.getLong("noteId", -1L) ?: -1L
if (noteId != -1L) loadNote(noteId) else showEmptyForm()Caller कुछ न भेजे तो graph की default values (जैसे -1L) use होती हैं।
Safe Args (type-safe तरीका)
Kotlin DSL – plugin जोड़ें
// build.gradle.kts (root)
plugins {
// ...
id("androidx.navigation.safeargs.kotlin") version "2.8.5" apply false
}
// app/build.gradle.kts
plugins {
// ...
id("androidx.navigation.safeargs.kotlin")
}Kotlin – generated Directions और Args
// nav_graph.xml has the global action action_global_editor → editorFragment // and editorFragment declares <argument android:name="noteId" app:argType="long" android:defaultValue="-1L"/> // Generated for global actions: NavGraphDirections (named after the graph id "nav_graph") val action = NavGraphDirections.actionGlobalEditor(noteId = note.id) findNavController().navigate(action) // Generated for the destination class: AddEditNoteFragmentArgs private val args: AddEditNoteFragmentArgs by navArgs() val noteId: Long = args.noteId // a Long – checked by the compiler
Toolbar Up button
Kotlin – MainActivity
// MainActivity (Student Diary)
val navHost = supportFragmentManager.findFragmentById(R.id.nav_host) as NavHostFragment
navController = navHost.navController
appBarConfiguration = AppBarConfiguration(topLevelIds, binding.drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration) // title + ← / ☰
override fun onSupportNavigateUp(): Boolean =
navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
// react to screen changes (hide bottom bar on the editor)
navController.addOnDestinationChangedListener { _, destination, _ ->
binding.bottomNav.isVisible = destination.id in bottomScreens
}आम गलतियाँ
- Menu item ids destination ids से मेल न खाना → bottom nav / drawer clicks कुछ नहीं करते।
navigate()जल्दी-जल्दी दो बार (double tap) → "navigation destination is unknown" crash। Extra clicks ignore करें या current destination check करें।onSupportNavigateUpभूलना → ← button कुछ नहीं करता।- Long arguments का गलत default format →
-1Lलिखें।
सारांश
- nav_graph.xml destinations, actions और arguments list करता है।
- NavHostFragment screens host करता है;
findNavController().navigate(id, bundle)उनके बीच ले जाता है। - Safe Args type-safe Directions/Args बनाता है;
setupActionBarWithNavControllertitles और Up संभालता है।
✅ Practice
Fragment demo को nav graph में बदलिए: Home → Details (argument: subject) → Edit। Safe Args और actions पर enter/exit animation जोड़िए।
अक्सर पूछे जाने वाले प्रश्न (FAQ)
Destination क्या है?
Navigation graph की एक screen, आमतौर पर fragment। हर destination की एक id होती है जिस पर navigate करते हैं।
क्या Safe Args ज़रूरी है?
नहीं। bundleOf() से Bundle भेज सकते हैं। Safe Args classes generate करता है ताकि compiler argument names और types check करे, जिससे typos नहीं होते।
Code से वापस कैसे जाएँ?
findNavController().navigateUp() या popBackStack() call करें। दोनों पिछली destination पर लौटाते हैं।
