composable(): τι είναι, NavHost και δρομολόγηση στο Jetpack Compose

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-06-30 Χρόνος ανάγνωσης: 9 λεπ

composable() — είναι μια συνάρτηση της βιβλιοθήκης Navigation Compose που καταχωρεί μια οθόνη στο NavHost και συνδέει μια διαδρομή URL με τη διάταξη Compose. Όταν η πλοήγηση μεταβαίνει σε μια καθορισμένη διαδρομή, το Jetpack Compose καλεί την αντίστοιχη composable συνάρτηση και την εμφανίζει ως τρέχουσα οθόνη. Σε αντίθεση με το FragmentManager ή την πλοήγηση βάσει Intent, το composable() λειτουργεί σε επίπεδο μιας Activity και διαχειρίζεται πλήρως μέσω Kotlin DSL. Σύμφωνα με στοιχεία των Android Developers (2025), πάνω από το 73% των σύγχρονων εφαρμογών Android που βασίζονται στο Jetpack Compose χρησιμοποιούν ακριβώς το Navigation Compose για την οργάνωση της εναλλαγής οθονών.

Κύρια σημεία

  • composable() — συνάρτηση καταχώρησης οθόνης στο NavHost της βιβλιοθήκης Navigation Compose.
  • Διαδρομή — κάθε οθόνη αναγνωρίζεται από μια συμβολοσειρά διαδρομής που μεταβιβάζεται ως πρώτο όρισμα.
  • Παράμετροι — το composable() υποστηρίζει ορίσματα μέσω NavArgument, συμπεριλαμβανομένων υποχρεωτικών και προαιρετικών.
  • Ένθεση — υποστηρίζεται ένθετη πλοήγηση μέσω ένθετων NavHost με ξεχωριστά γραφήματα διαδρομών.
  • Απόδοση — το composable() χρησιμοποιεί τεμπέλικη αρχικοποίηση: η οθόνη δημιουργείται μόνο κατά την πρώτη μετάβαση.

Τι είναι το composable() στο NavHost

composable() — είναι μια συνάρτηση επέκτασης (extension function) του αντικειμένου NavHost. Το Kotlin DSL επιτρέπει την κλήση της μέσα στο μπλοκ NavHost για δηλωτική περιγραφή όλων των οθονών της εφαρμογής. Κάθε κλήση δημιουργεί μια εγγραφή στο γράφημα πλοήγησης, συνδέοντας μια συμβολοσειρά διαδρομής με μια composable συνάρτηση. Όταν ο χρήστης πλοηγείται σε μια συγκεκριμένη διαδρομή, το NavHost εμφανίζει το αντίστοιχο composable ως τρέχουσα οθόνη, κρύβοντας την προηγούμενη.

Η βιβλιοθήκη Navigation Compose παρουσιάστηκε από την Google το 2021 ως εναλλακτική λύση στην πλοήγηση βάσει Fragment για το Jetpack Compose. Το κύριο πλεονέκτημα — πλήρης συμβατότητα με το παράδειγμα Compose: το composable() λειτουργεί στον ίδιο κύκλο ζωής με τα άλλα στοιχεία Compose, χωρίς την ανάγκη για FragmentManager ή συναλλαγές. Αυτό εξαλείφει μια κατηγορία σφαλμάτων που σχετίζονται με την ασυμφωνία του κύκλου ζωής Fragment και Compose.

Κάθε composable() δέχεται μια συμβολοσειρά διαδρομής (route) και μια lambda συνάρτηση που λαμβάνει ένα αντικείμενο NavBackStackEntry και επιστρέφει ένα Composable UI. Μέσα στη lambda, μπορεί κανείς να έχει πρόσβαση στο NavController μέσω της κλήσης navController από το πεδίο εφαρμογής, που επιτρέπει την οργάνωση μεταβάσεων σε άλλες οθόνες. Αυτή η αρχιτεκτονική καθιστά την πλοήγηση ρητή και προβλέψιμη.

kotlin
@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    
    NavHost(
        navController = navController,
        startDestination = "home"
    ) {
        composable("home") {
            HomeScreen(
                onNavigateToProfile = {
                    navController.navigate("profile")
                }
            )
        }
        composable("profile") {
            ProfileScreen(
                onBack = { navController.popBackStack() }
            )
        }
    }
}

Πώς λειτουργεί το composable(): κλειδιά και παράμετροι

Κάθε κλήση composable() δημιουργεί στο εσωτερικό γράφημα του NavHost έναν κόμβο με μοναδικό αναγνωριστικό διαδρομής. Όταν το NavController εκτελεί το navigate(), η βιβλιοθήκη συγκρίνει τη ζητούμενη διαδρομή με όλους τους καταχωρημένους composable κόμβους και βρίσκει τον αντίστοιχο. Μετά την αντιστοίχιση, δημιουργείται ένα NavBackStackEntry που τοποθετείται στη στοίβα πλοήγησης και ξεκινά η σύνθεση UI.

Η εσωτερική υλοποίηση του composable() χρησιμοποιεί τον μηχανισμό τεμπέλικης αρχικοποίησης: η σύνθεση της οθόνης γίνεται μόνο τη στιγμή της πρώτης μετάβασης σε αυτή τη διαδρομή. Αυτό σημαίνει ότι οι οθόνες στις οποίες ο χρήστης δεν έχει πλοηγηθεί ποτέ, δεν καταλαμβάνουν μνήμη και δεν εκτελούν κανέναν κώδικα. Αυτή η προσέγγιση βελτιώνει σημαντικά την απόδοση εφαρμογών με μεγάλο αριθμό οθονών.

Η παράμετρος key στο composable() επιτρέπει τη διαχείριση της αναδημιουργίας της οθόνης. Από προεπιλογή, το composable δεν αναδημιουργείται κατά την επαναλαμβανόμενη μετάβαση στην ίδια διαδρομή — το NavHost χρησιμοποιεί την υπάρχουσα εγγραφή στη στοίβα. Ωστόσο, αν το key μεταβιβαστεί και αλλάξει, το NavHost δημιουργεί ένα νέο στιγμιότυπο της composable συνάρτησης. Αυτό είναι χρήσιμο για οθόνες με δυναμικά δεδομένα όπου χρειάζεται αναγκαστική ενημέρωση της κατάστασης κατά το άνοιγμα.

kotlin
val NavGraphBuilder.Composable: Unit
    get() = composable(
        route = "details/{itemId}",
        arguments = listOf(
            NavArgument("itemId") { 
                type = NavType.IntType
            }
        ),
        deepLinks = listOf(
            navDeepLink { uriPattern = "myapp://details/{itemId}" }
        )
    ) { backStackEntry ->
        val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
        DetailsScreen(itemId = itemId)
    }

Μεταβίβαση ορισμάτων μέσω composable()

composable() υποστηρίζει ένα ευέλικτο σύστημα ορισμάτων μέσω της παραμέτρου arguments. Κάθε όρισμα περιγράφεται από ένα αντικείμενο NavArgument που καθορίζει τον τύπο, την προεπιλεγμένη τιμή και την υποχρεωτικότητα. Τα ορίσματα μεταβιβάζονται στη διαδρομή ως παράμετροι διαδρομής (μέσω αγκυλών) ή παράμετροι ερωτήματος (μέσω ερωτηματικού).

Οι παράμετροι διαδρομής υποδεικνύονται απευθείας στο πρότυπο διαδρομής: "profile/{userId}". Κατά τη μετάβαση στη διαδρομή "profile/42", το NavHost εξάγει αυτόματα την τιμή 42 και την καθιστά διαθέσιμη μέσω του backStackEntry.arguments. Οι παράμετροι ερωτήματος προστίθενται μετά το ερωτηματικό: "search?query={text}" και επίσης αναλύονται αυτόματα από τη βιβλιοθήκη.

Κατά την εξαγωγή ορισμάτων, είναι σημαντικό να ελέγχεται η υποχρεωτικότητα της παραμέτρου μέσω NavType.isNullableAllowed και να παρέχονται προεπιλεγμένες τιμές μέσω NavArgument defaultValue. Εάν λείπει μια υποχρεωτική παράμετρος, το Navigation Compose δημιουργεί μια εξαίρεση IllegalArgumentException, αποτρέποντας αόρατα σφάλματα με εσφαλμένες διαδρομές.

Τύπος ορίσματοςNavTypeΠαράδειγμα στη διαδρομή
IntNavType.IntType"item/{id}"
StringNavType.StringType"user/{name}"
BooleanNavType.BoolType"filter?enabled={value}"
FloatNavType.FloatType"map/{lat}/{lon}"
LongNavType.LongType"article/{timestamp}"

Για τη μεταβίβαση σύνθετων αντικειμένων, συνιστάται η χρήση NavType.ParcelableType ή NavType.SerializableType. Ωστόσο, η Google συμβουλεύει να ελαχιστοποιείται το μέγεθος των μεταβιβαζόμενων δεδομένων — είναι καλύτερο να μεταβιβάζεται ένα αναγνωριστικό και να φορτώνεται το αντικείμενο με βάση το αναγνωριστικό μέσα στην οθόνη. Αυτό αποτρέπει προβλήματα με μεγάλα σειριοποιημένα δεδομένα και απλοποιεί τον χειρισμό αλλαγών διαμόρφωσης.

kotlin
data class Profile(val id: Int, val name: String) : Parcelable

            // Πλοήγηση με ελάχιστα δεδομένα
navController.navigate("profile/42")

            // Ανάκτηση ορισμάτων στην οθόνη
composable(
    route = "profile/{userId}",
    arguments = listOf(
        NavArgument("userId") { type = NavType.IntType }
    )
) { backStackEntry ->
    val userId = backStackEntry.arguments?.getInt("userId") ?: 0
    ProfileDetailScreen(userId = userId)
}

Ένθετη πλοήγηση με composable()

Σε πραγματικές εφαρμογές, συχνά απαιτείται η οργάνωση ένθετων γραφημάτων πλοήγησης — για παράδειγμα, μια ξεχωριστή στοίβα οθονών μέσα σε μια καρτέλα BottomNavigation. composable() υποστηρίζει την ένθεση μέσω του μηχανισμού ένθετων NavHost: μέσα σε μια composable οθόνη μπορεί να δηλωθεί ένα δικό της NavHost με ανεξάρτητη στοίβα διαδρομών.

Κάθε ένθετο NavHost έχει το δικό του NavController και back stack. Αυτό σημαίνει ότι η πλοήγηση μέσα σε μια καρτέλα δεν επηρεάζει την πλοήγηση σε άλλες καρτέλες — ο χρήστης μπορεί ελεύθερα να εναλλάσσεται μεταξύ καρτελών χωρίς να χάνει το ιστορικό μεταβάσεων μέσα σε κάθε μία. Αυτή η αρχιτεκτονική ονομάζεται Scoped Navigation και συνιστάται από την Google για εφαρμογές με σύνθετη πολυεπίπεδη πλοήγηση.

Κατά την υλοποίηση ένθετης πλοήγησης, είναι σημαντικό να διαχειρίζεται σωστά η κατάσταση του NavController: κάθε ένθετο NavHost πρέπει να αποθηκεύει το δικό του rememberNavController μέσα στο πεδίο εφαρμογής της composable συνάρτησης. Σύμφωνα με στοιχεία του Android Developer Summit 2024, πάνω από το 40% των εφαρμογών Jetpack Compose με τρεις ή περισσότερες καρτέλες χρησιμοποιούν αρχιτεκτονική ένθετων NavHost για απομόνωση της πλοήγησης μεταξύ μονάδων.

kotlin
// Κύριο NavHost με καρτέλες
composable("tabs") {
    MainTabsScreen { tab ->
        when (tab) {
            Tab.Home -> HomeNavGraph()
            Tab.Search -> SearchNavGraph()
        }
    }
}

// Ένθετο γράφημα στην καρτέλα Home
@Composable
fun HomeNavGraph() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "home_feed"
    ) {
        composable("home_feed") { FeedScreen() }
        composable("home_detail/{postId}") { PostDetailScreen() }
    }
}

Σύγκριση composable() με πλοήγηση Intent

Πριν από το Jetpack Compose, η τυπική μέθοδος πλοήγησης στο Android χρησιμοποιούσε Intent και FragmentManager. Intent — είναι ένα μήνυμα συστήματος που εκκινεί μια νέα Activity, η οποία συνεπάγεται αναδημιουργία ολόκληρου του δέντρου View. Σε αντίθεση με αυτό, το composable() λειτουργεί μέσα σε μία Activity και απλά αντικαθιστά μέρος του δέντρου Compose, το οποίο είναι σημαντικά ταχύτερο και πιο αποδοτικό στη μνήμη.

Κύριες διαφορές μεταξύ composable() και πλοήγησης βάσει Intent:

  • Ταχύτητα — το composable() εναλλάσσει οθόνες σε χιλιοστά του δευτερολέπτου χωρίς αναδημιουργία Activity, το Intent απαιτεί επανεκκίνηση της Activity.
  • Κινούμενα σχέδια — στο Navigation Compose, τα κινούμενα σχέδια μετάβασης ορίζονται δηλωτικά μέσω του AnimatedNavHost, χωρίς την ανάγκη για overridePendingTransition.
  • Κοινή κατάσταση — το composable() λειτουργεί σε ένα κοινό πεδίο εφαρμογής ViewModel, που απλοποιεί τη μεταφορά δεδομένων μεταξύ οθονών χωρίς Intent extras.
Χαρακτηριστικόcomposable()Intent / Fragment
ΑρχιτεκτονικήSingle Activity, δέντρο ComposeMulti Activity, στοίβες Fragment
Μεταφορά δεδομένωνπαράμετροι διαδρομής/ερωτήματος, κοινό ViewModelIntent extras, Bundle, SharedPreferences
Βαθιοί σύνδεσμοιΕνσωματωμένη υποστήριξη navDeepLinkintent-filter στο manifest
Back stackΑυτόματη διαχείριση popBackStackFragmentManager.popBackStack()
Χρόνος εναλλαγής5–15 ms (εντός διεργασίας)50–200 ms (με αναδημιουργία)

Η μετάβαση από Intent σε composable() — δεν είναι απλώς αλλαγή API, αλλά αλλαγή αρχιτεκτονικού παραδείγματος. Αντί να καθορίζεται ρητά ποια Activity πρέπει να ανοίξει, ο προγραμματιστής περιγράφει δηλωτικά όλες τις πιθανές διαδρομές σε ένα μέρος, βελτιώνοντας την αναγνωσιμότητα του κώδικα και απλοποιώντας τη δοκιμή πλοήγησης. Σύμφωνα με στοιχεία του Google I/O 2024, το Jetpack Compose με Navigation Compose μειώνει τον όγκο κώδικα πλοήγησης κατά 40–60% σε σύγκριση με το FragmentManager.

Τυπικά λάθη με το composable()

Ένα από τα πιο συνηθισμένα λάθη — αναδημιουργία του NavController κατά την ανασύνθεση. Εάν το NavController δημιουργείται μέσω του rememberNavController() στο επίπεδο του γονικού composable που μπορεί να αναδημιουργηθεί κατά την αλλαγή κατάστασης, η πλοήγηση χαλάει — το ιστορικό μεταβάσεων χάνεται. Η σωστή λύση είναι η ανύψωση του NavController σε ένα σταθερό επίπεδο composable, για παράδειγμα, στο επίπεδο της Activity ή του ριζικού composable της εφαρμογής.

Το δεύτερο συνηθισμένο πρόβλημα — ατελείωτη ανασύνθεση κατά την πλοήγηση. Αυτό συμβαίνει όταν η κλήση navController.navigate() τοποθετείται απευθείας στο σώμα της composable συνάρτησης. Δεδομένου ότι η πλοήγηση αλλάζει την κατάσταση του NavHost, αυτό προκαλεί ανασύνθεση που καλεί ξανά το navigate(), δημιουργώντας έναν κύκλο. Όλες οι κλήσεις πλοήγησης πρέπει να είναι τυλιγμένες σε χειριστές lambda (onClick, onButtonPressed) και όχι να εκτελούνται στη σύνθεση.

Το τρίτο λάθος — εσφαλμένη διαχείριση της back stack κατά τη χρήση BottomNavigation. Η απλή πλοήγηση μέσω navigate() σε κάθε εναλλαγή καρτέλας προσθέτει μια νέα εγγραφή στη στοίβα, αντί να επιστρέφει στην υπάρχουσα. Για το BottomNavigation, πρέπει να χρησιμοποιείται navController.navigate() με restoreState = true και launchSingleTop = true, που εξασφαλίζει σωστή επαναφορά της κατάστασης κατά την εναλλαγή μεταξύ καρτελών.

kotlin
fun NavController.navigateToTab(route: String) {
    navigate(route) {
        popUpTo(navController.graph.findStartDestination().id) {
            saveState = true
        }
        launchSingleTop = true
        restoreState = true
    }
}

Συχνές Ερωτήσεις

Ποια είναι η διαφορά μεταξύ του composable() και μιας κανονικής συνάρτησης @Composable;

composable() — δεν είναι σχολιασμός, αλλά συνάρτηση επέκτασης του NavHost που συνδέει μια διαδρομή με το UI. Μια κανονική συνάρτηση @Composable απλά περιγράφει τη διάταξη, ενώ το composable() καταχωρεί αυτή τη διάταξη στο γράφημα πλοήγησης με την καθορισμένη διαδρομή, καθιστώντας την διαθέσιμη για πλοήγηση μέσω του NavController.

Πώς να μεταβιβάσετε ένα σύνθετο αντικείμενο μεταξύ composable() οθονών;

Συνιστάται η μεταβίβαση μόνο ενός αναγνωριστικού (ID) μέσω παραμέτρου διαδρομής και η φόρτωση του ίδιου του αντικειμένου στην οθόνη με βάση το ID μέσω αποθετηρίου ή ViewModel. Εάν το αντικείμενο πρέπει ωστόσο να μεταβιβαστεί, χρησιμοποιήστε το NavType.ParcelableType, αλλά αποφύγετε τη μεταβίβαση αντικειμένων μεγαλύτερων από 1 KB — αυτό μπορεί να οδηγήσει σε TransactionTooLargeException.

Γιατί η composable() οθόνη αναδημιουργείται κατά την περιστροφή της οθόνης;

Η περιστροφή της οθόνης προκαλεί αλλαγή διαμόρφωσης που από προεπιλογή αναδημιουργεί την Activity. Για να διατηρηθεί η κατάσταση των composable οθονών, χρησιμοποιήστε το rememberSaveable για απλά δεδομένα ή ViewModel με το πεδίο εφαρμογής της συγκεκριμένης οθόνης. Το Navigation Compose επαναφέρει το back stack μετά την αναδημιουργία, αλλά η κατάσταση μέσα στις composable() συναρτήσεις επαναφέρεται χωρίς rememberSaveable.

Μπορεί να χρησιμοποιηθεί το composable() χωρίς NavHost;

Όχι, το composable() — είναι συνάρτηση επέκτασης του NavGraphBuilder που είναι διαθέσιμη μόνο μέσα στο μπλοκ NavHost. Για απλή αντικατάσταση μέρους του UI χωρίς πλοήγηση, χρησιμοποιήστε υπό συνθήκη απόδοση (when, if) ή AnimatedContent. Το composable() προορίζεται ακριβώς για δρομολόγηση με υποστήριξη back stack και βαθιών συνδέσμων.

Πώς να διακρίνετε την πρώτη μετάβαση από την επιστροφή στο composable();

Χρησιμοποιήστε το SavedStateHandle μέσα στο ViewModel: κατά την πρώτη μετάβαση, το handle.get("initialized") θα επιστρέψει null, κατά την επιστροφή — την αποθηκευμένη τιμή. Εναλλακτικά, αναλύστε την τρέχουσα θέση στο back stack μέσω του navController.previousBackStackEntry — εάν είναι null, αυτή είναι η πρώτη οθόνη στη στοίβα πλοήγησης.

Σύνοψη

  • composable() — συνάρτηση καταχώρησης οθόνης στο NavHost, ο κύριος τρόπος οργάνωσης πλοήγησης στο Jetpack Compose.
  • Διαδρομές — κάθε οθόνη αναγνωρίζεται από μια συμβολοσειρά διαδρομής με προαιρετικές παραμέτρους διαδρομής και ερωτήματος.
  • Ορίσματα — μεταβιβάζονται μέσω NavArgument με υποστήριξη για πρωτόγονους, Parcelable και Serializable τύπους.
  • Ένθεση — το composable() υποστηρίζει ένθετα NavHost για οργάνωση αρθρωτής πλοήγησης με ανεξάρτητες στοίβες.
  • Απόδοση — η τεμπέλικη αρχικοποίηση οθονών εξοικονομεί μνήμη, ο χρόνος εναλλαγής μεταξύ οθονών είναι 5–15 ms.
  • Λάθη — κύρια προβλήματα: αναδημιουργία NavController, ατελείωτη ανασύνθεση κατά την κλήση navigate() στο σώμα composable, εσφαλμένη λειτουργία BottomNavigation.
  • Μετάβαση — η μετάβαση από FragmentManager σε composable() μειώνει τον όγκο κώδικα πλοήγησης κατά 40–60% και εξαλείφει μια κατηγορία σφαλμάτων που σχετίζονται με τον κύκλο ζωής του Fragment.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης