Navigation Compose — to biblioteka Jetpack do deklaratywnej nawigacji w aplikacjach Android zbudowanych na Jetpack Compose. Zamiast FragmentManager lub nawigacji opartej na Intent, Navigation Compose oferuje jednolity graf tras zarządzany przez NavController i NavHost. Według danych Google I/O (2025), Navigation Compose to zalecany sposób nawigacji dla aplikacji Compose, używany w ponad 70% nowych projektów. Biblioteka obsługuje przekazywanie typowanych argumentów, głębokie linki, animacje przejść i integrację z ViewModel przez SavedStateHandle.
Najważniejsze
Navigation Compose — to biblioteka z pakietu Jetpack, zapewniająca framework nawigacyjny dla aplikacji Compose. Biblioteka opiera się na tych samych zasadach co Navigation Component dla systemu View, ale jest dostosowana do deklaratywnej natury Compose: zamiast FragmentTransaction używane są funkcje composable, a graf nawigacji budowany jest przez Kotlin DSL.
Kluczowa różnica między Navigation Compose a klasyczną nawigacją — brak FragmentManager. Każdy ekran to funkcja composable, która renderuje się w NavHost po dopasowaniu trasy. Back stack przechowuje nie Fragment, ale wpis z route, argumentami i stanem. Upraszcza to architekturę i eliminuje konflikty cyklu życia charakterystyczne dla nawigacji Fragment.
Według danych Google (2025), Navigation Compose przeszedł drogę od experimental do stable i wchodzi w skład Jetpack od wersji 2.8.0. Biblioteka obsługuje Material3, Type-Safe Navigation (przez Kotlin Serialization), zagnieżdżone grafy i modularyzację. Jedynym ograniczeniem jest brak obsługi multi-back stack dla BottomNavigation bez ręcznej konfiguracji, choć Google pracuje nad tym.
Architektura Navigation Compose opiera się na trzech jednostkach: NavController (zarządzanie stosem), NavHost (kontener grafu) i NavDestination (pojedyncza trasa z composable). Interakcja między nimi jest deklaratywna: programista opisuje trasy i argumenty, a biblioteka obsługuje stany ładowania, preserve i restore.
NavController — centralny element Navigation Compose, zarządzający stosem nawigacyjnym. Tworzony jest przez rememberNavController() i przekazywany do NavHost. NavController przechowuje back stack, bieżący punkt wejścia i obsługuje opóźnione działania (deeplink po inicjalizacji grafu).
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("profile/{userId}") { backStackEntry ->
ProfileScreen(
userId = backStackEntry.arguments?.getString("userId") ?: ""
)
}
}
}
Główne metody NavController: navigate(route) — przejście do trasy, popBackStack() — powrót do poprzedniego ekranu, navigateAndClear(route) — przejście z czyszczeniem stosu. NavOptions określają zachowanie: launchSingleTop zapobiega duplikowaniu, popUpTo czyści stos do określonej trasy, restoreState przywraca poprzedni stan.
Dostęp do NavController z głęboko zagnieżdżonych funkcji composable uzyskuje się przez NavHostController z CompositionLocal. LocalNavController jest udostępniany w ScopedNavController wewnątrz NavHost. Poza NavHost (np. w BottomNavigation) kontroler przekazywany jest przez parametry lub ViewModel.
NavHost — to kontener composable, który łączy NavController z grafem tras. Każda trasa deklarowana jest przez composable(route, arguments, deepLinks), gdzie route to ciąg trasy z opcjonalnymi placeholderami {param}. Po dopasowaniu bieżącej trasy do route, NavHost renderuje odpowiedni blok composable.
Graf tras budowany jest hierarchicznie: można zagnieżdżać grafy przez navigation() do grupowania tras wewnątrz modułu. Zagnieżdżone grafy mają własny startDestination i są łączone pod wspólnym prefiksem route. Pozwala to na zorganizowanie architektury modułowej, gdzie każdy feature-moduł rejestruje swój podgraf.
NavHost(
navController = navController,
startDestination = "main"
) {
composable("main") { MainScreen(navController) }
navigation(
route = "auth",
startDestination = "auth/login"
) {
composable("auth/login") { LoginScreen(navController) }
composable("auth/register") { RegisterScreen(navController) }
}
}
NavHost automatycznie obsługuje systemowy przycisk Wstecz (back press) przez LocalBackDispatcher. W Material3 Scaffold domyślnie przechwytuje NavController do poprawnej pracy BottomNavigation. NavHost odtwarza composable przy zmianie trasy, ale zachowuje stan przez rememberSaveable dla pól wejściowych i scrolla.
Navigation Compose obsługuje przekazywanie typowych argumentów między ekranami przez parametry trasy i NavType. Parametry określa się w route jako {paramName} z podaniem typu przez arguments w composable(). NavType obsługuje String, Int, Long, Float, Boolean, Parcelable i Serializable.
| Typ argumentu | NavType | Przykład route |
|---|---|---|
| String | NavType.StringType | "profile/{name}" |
| Int | NavType.IntType | "item/{id}" |
| Boolean | NavType.BoolType | "settings?enabled={flag}" |
| Parcelable | NavType.ParcelableType | "details/{item}" |
| Float | NavType.FloatType | "map?lat={lat}&lng={lng}" |
Argumenty są pobierane z NavBackStackEntry przez arguments?.getType(key). Dla obowiązkowych parametrów używaj defaultValue, dla opcjonalnych — nullable. Obsługa Parcelable działa tylko z Kotlin Parcelize lub biblioteką kotlinx.parcelize. Dla złożonych obiektów zaleca się przekazywanie ID i ładowanie danych przez ViewModel, a nie serializację całego obiektu.
Od wersji Navigation 2.8.0 dostępna jest Type-Safe Navigation z Kotlin Serialization: trasy definiowane są jako data-klasy, a argumenty jako pola. Zastępuje to ciągowe route typowanymi obiektami i eliminuje błędy w nazwach tras. Do migracji wymagany jest plugin Kotlin Serialization i zależność navigation-compose-typesafe.
@Serializable
/* sealed class Route */
sealed class ProfileRoute(val route: String) {
data object Home : ProfileRoute("home")
data class Profile(val userId: String) : ProfileRoute("profile/{userId}")
}
Deep Links — mechanizm nawigacji umożliwiający otwarcie określonego ekranu aplikacji przez URL lub intent-filter. W Navigation Compose deep linki konfiguruje się przez parametr deepLinks w composable() i są one obsługiwane automatycznie po dopasowaniu URI do wzorca.
Deep Link określany jest jako lista UriPattern: "https://example.com/profile/{userId}". Parametry z URI są automatycznie mapowane na argumenty trasy. NavController obsługuje deep link przy uruchomieniu aplikacji (przez intent) i w trakcie działania (przez implicit deep links). Do obsługi oczekujących deep linków używa się handleDeepLink() w NavController po inicjalizacji grafu.
Według Google, deep linki zaleca się konfigurować dla: powiadomień push (Firebase Dynamic Links), weryfikacji email, udostępniania treści i nawigacji z linków webowych. Dla Android 12+ używa się Digital Asset Links do weryfikacji autorytatywności deep linka. AndroidManifest.xml musi zawierać intent-filter z autoVerify="true" do otwierania linków bez dialogu.
composable(
route = "profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType }),
deepLinks = listOf(
navDeepLink { uriPattern = "https://example.com/profile/{userId}" }
)
) { backStackEntry ->
ProfileScreen(userId = backStackEntry.arguments?.getString("userId") ?: "")
}
Ograniczenia deep linków w Navigation Compose: biblioteka nie obsługuje deferred deep links — deep link jest obsługiwany dopiero po pełnym zbudowaniu grafu przez NavHost. Jeśli deep link nadejdzie przed inicjalizacją grafu, należy go odłożyć przez intent?.data i obsłużyć w LaunchedEffect. Dla Firebase Dynamic Links używaj Firebase Dynamic Links SDK w połączeniu z Navigation Compose.
Navigation Compose obsługuje animację przejść przez parametry enterTransition, exitTransition, popEnterTransition i popExitTransition w composable(). Animacje realizowane są przez Compose Animation API: fadeIn, slideInHorizontally, expandIn i inne. Domyślnie animacja jest wyłączona — ekrany zastępują się natychmiast.
Typowe scenariusze animacji: slideInHorizontally do nawigacji w przód (ekran wjeżdża z prawej), slideOutHorizontally do powrotu (ekran wyjeżdża w prawo). Dla BottomNavigation częściej stosuje się animację fade bez przesuwania. Animacje ustawiane są przez NavHost i stosowane do wszystkich composable, jeśli nie określono indywidualnych.
NavHost(
navController = navController,
startDestination = "home",
enterTransition = { slideInHorizontally() + fadeIn() },
exitTransition = { slideOutHorizontally() + fadeOut() },
popEnterTransition = { fadeIn() },
popExitTransition = { slideOutHorizontally() + fadeOut() }
) { /* composable */ }
Animacje można nadpisać dla każdego composable indywidualnie, przekazując parametry animacyjne bezpośrednio w composable(). Ważne: animacje nie powinny kolidować z systemową animacją back press. Do shared element transition wymagana jest biblioteka accompanist-navigation-animation lub własna implementacja przez Modifier.graphicsLayer. Według Android Developers (2025), 80% produkcyjnych aplikacji używa poziomej animacji slide dla standardowej nawigacji.
Często zadawane pytania
Navigation Compose działa bez Fragment, używając funkcji composable i Kotlin DSL do grafu. Navigation Component (View) opiera się na FragmentManager i grafach XML. Wersja Compose jest prostsza, szybsza i nie ma cykli życia Fragment. Navigation Component dla View nadaje się tylko do aplikacji hybrydowych.
Zaleca się przekazywanie ID obiektu i ładowanie danych przez ViewModel z SavedStateHandle. Jeśli obiekt jest prosty — używaj Parcelable przez kotlinx.parcelize. Przekazywanie dużych obiektów bezpośrednio przez argumenty (Bundle) jest ograniczone do ~1 MB i może spowodować TransactionTooLargeException.
Tak, przez funkcję navigation(route, startDestination) wewnątrz NavHost. Zagnieżdżone grafy mają własny startDestination i są łączone pod wspólnym prefiksem route. Pozwala to na zorganizowanie architektury modułowej z izolowanymi grafami dla każdego feature-modułu.
NavController automatycznie obsługuje back press przez BackHandler z Compose. Wywołaj navController.popBackStack() po naciśnięciu Wstecz. Do własnej obsługi (potwierdzenie wyjścia) użyj BackHandler(enabled = condition) { callback } przed wywołaniem popBackStack().
Obecnie Navigation Compose nie jest obsługiwany w Compose Multiplatform. Dla części iOS w projektach cross-platformowych używaj Voyager lub Decompose. Google pracuje nad obsługą KMP, ale terminy nie są znane. Dla projektów Android-only Navigation Compose jest jedynym zalecanym rozwiązaniem.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również