- Το Flutter και το Jetpack Compose μοιράζονται ένα δηλωτικό, αντιδραστικό μοντέλο UI, αλλά διαφέρουν ως προς τη γλώσσα, το οικοσύστημα και την εμβέλεια της πλατφόρμας.
- Η σύνθεση αντιστοιχίζεται καθαρά σε έννοιες Flutter: οι συνθέσεις σε widgets, οι Lazy λίστες σε ListView/GridView, ο Canvas σε CustomPainter και τα θέματα σε ThemeData.
- Οι δεξιότητες που είναι εγγενείς σε Android (κύκλος ζωής, πλοήγηση, πόροι, ταυτόχρονη χρήση) μεταφέρονται απευθείας στο Flutter μέσω widgets, Navigator, assets και async/wait.
- Για έργα μόνο για Android, το Compose ξεχωρίζει, ενώ το Flutter υπερέχει όταν χρειάζεστε μια ενιαία βάση κώδικα για Android, iOS, web και υπολογιστή.

Αν ήδη νιώθετε άνετα να γράφετε UI με το Jetpack Compose και αναρωτιέστε πόσο δύσκολο είναι να μεταβείτε στο Flutter, τότε βρίσκεστε σε εξαιρετική θέση. Και τα δύο εργαλεία είναι δηλωτικά, αντιδραστικά και έχουν κατασκευαστεί από την Google, επομένως ένα τεράστιο μέρος του νοητικού σας μοντέλου μεταφέρεται σχεδόν ένα προς ένα. Οι κύριες διαφορές βρίσκονται στη γλώσσα (Kotlin vs Dart), στη δομή του έργου και στον τρόπο με τον οποίο κάθε πλαίσιο επικοινωνεί με τα υποκείμενα επίπεδα Android (και, στην περίπτωση του Flutter, iOS, web και desktop).
Αυτός ο οδηγός έχει γραφτεί ειδικά για προγραμματιστές του Jetpack Compose που θέλουν να κατανοήσουν σε βάθος το Flutter, χωρίς περιττά διαφημιστικά μηνύματα. Θα δείτε πώς οι βασικές έννοιες αντιστοιχίζονται μεταξύ των δύο κόσμων: composables vs widgets, τροποποιητές vs παράμετροι constructor, Lazy layouts vs ListView/GridView, Canvas vs CustomPainter, Navigation Compose vs Navigator, remember vs StatefulWidget και άλλα. Θα συνδέσουμε επίσης το ευρύτερο υπόβαθρο Android σας (Views, κύκλος ζωής, πόροι, intents, εργασία στο παρασκήνιο) με τα αντίστοιχα Flutter, ώστε η καμπύλη εκμάθησης να μοιάζει περισσότερο με ένα πλάγιο σκαλοπάτι παρά με μια ανάβαση.
Από το Jetpack Compose στο Flutter: όπου μεταφέρονται οι δεξιότητές σας
Το Flutter είναι το πλαίσιο χρήστη της Google για τη δημιουργία εφαρμογών σε διάφορες πλατφόρμες χρησιμοποιώντας τη γλώσσα Dart, ενώ το Jetpack Compose είναι το σύγχρονο κιτ εργαλείων χρήστη της Google για εγγενές Android που χρησιμοποιεί Kotlin. Στο εσωτερικό τους στοχεύουν σε διαφορετικούς χρόνους εκτέλεσης, αλλά αρχιτεκτονικά μοιράζονται την ίδια μεγάλη ιδέα: περιγράψτε το UI ως συνάρτηση της κατάστασης, αφήστε το πλαίσιο να καταλάβει πότε και πώς να το επανασχεδιάσει.
Στο Jetpack Compose σκέφτεστε με βάση τις συναρτήσεις που μπορούν να συντεθούν, τους τροποποιητές και την ανασύνθεση, ενώ στο Flutter σκέφτεστε με βάση τα widgets, τις παραμέτρους του κατασκευαστή και τις ανακατασκευές. Παρά τη διαφορετική ονομασία, η συμπεριφορά είναι εντυπωσιακά παρόμοια: δημιουργείτε ένα δέντρο στοιχείων UI, κάθε κόμβος είναι αμετάβλητος και όταν αλλάζει η κατάσταση, το πλαίσιο περπατά ξανά σε αυτό το δέντρο για να παράγει μια ενημερωμένη διεπαφή.
Μια βασική διαφορά είναι ότι το Flutter είναι σχεδιασμένο για όλες τις πλατφόρμες. Η ίδια βάση κώδικα Dart μπορεί να στοχεύσει Android, iOS, web, Windows, macOS και Linux. Το Compose επεκτείνεται πέρα από το Android (για παράδειγμα με το Compose Multiplatform), αλλά η ιστορία πολλαπλών συσκευών του Flutter είναι πολύ πιο ώριμη και συνεκτική αυτή τη στιγμή, γι' αυτό και πολλές ομάδες που προσανατολίζονται στο Android το εξετάζουν όταν θέλουν να κυκλοφορήσουν σε iOS ή υπολογιστές.
Η κατανόησή σας για την ίδια την πλατφόρμα Android εξακολουθεί να είναι εξαιρετικά πολύτιμη στα έργα Flutter. Ενώ το επίπεδο UI αποτελείται από καθαρό Dart και widgets, το Flutter βασίζεται στο Android (και το iOS) για δικαιώματα, διαμόρφωση συστήματος, API πλατφόρμας, ειδοποιήσεις, εργασία στο παρασκήνιο και πολλές άλλες δυνατότητες, στις οποίες έχετε πρόσβαση μέσω plugins και καναλιών πλατφόρμας. Αυτό σημαίνει ότι όλη η διαίσθηση που έχετε δημιουργήσει σχετικά με τον τρόπο συμπεριφοράς του Android δεν πάει χαμένη—απλώς μετακινείται ένα επίπεδο προς τα κάτω.
Δηλωτικό μοντέλο UI: σύνθετα στοιχεία έναντι γραφικών στοιχείων
Τόσο το Jetpack Compose όσο και το Flutter εφαρμόζουν ένα δηλωτικό μοντέλο UI: περιγράφετε «πώς» θα πρέπει να μοιάζει το UI για μια δεδομένη κατάσταση, όχι «πώς» να μεταλλάσσονται οι προβολές βήμα προς βήμα. Αντί να καλείτε οριστές σε προβολές, ανακατασκευάζετε το δέντρο σας όταν αλλάζει η κατάσταση και αφήνετε το πλαίσιο να διαφοροποιείται και να επανασχεδιάζεται αποτελεσματικά.
Στο Jetpack Compose, τα στοιχεία UI είναι συναρτήσεις που μπορούν να συντεθούν και έχουν σχολιαστεί με @Composable, συχνά διαμορφωμένο με ένα Modifier. Ένα κουμπί μπορεί να είναι Button(onClick = ..., modifier = Modifier.padding(16.dp))Η αλυσίδα τροποποιητών διακοσμεί ή διαμορφώνει ένα συνθετέο στοιχείο χωρίς να αλλάζει τον υποκείμενο τύπο του και η Compose χρησιμοποιεί την ανασύνθεση για να ανανεώσει μόνο τα μέρη του δέντρου των οποίων τα δεδομένα εισόδου άλλαξαν.
Στο Flutter, τα στοιχεία UI είναι γραφικά στοιχεία—απλά αντικείμενα Dart που περιγράφουν τη διαμόρφωση. Είναι επίσης αμετάβλητα και διατεταγμένα σε δέντρο, αλλά αντί να περάσετε έναν τροποποιητή, συνήθως περνείτε ορίσματα διάταξης ή στυλ απευθείας μέσω παραμέτρων κατασκευαστή ή τυλίγετε ένα γραφικό στοιχείο σε άλλα γραφικά στοιχεία διάταξης. Για παράδειγμα, μπορείτε να γράψετε Padding(padding: EdgeInsets.all(16), child: ElevatedButton(...)) για να επιτευχθεί ένα παρόμοιο αποτέλεσμα.
Ο κύκλος ζωής τόσο των composables όσο και των widgets είναι σκόπιμα βραχύβιος και αμετάβλητος. Ζουν μόνο μέχρι να απαιτηθεί η αντικατάστασή τους από νέα δεδομένα. Κανένα από τα δύο δεν προσπαθεί να διαχειριστεί τη διάρκεια ζωής του ή να μεταλλαχθεί άμεσα. Αυτή είναι μια εννοιολογική αλλαγή από τον παλιό κόσμο του Android View, όπου οι προβολές είναι αντικείμενα μακράς διαρκείας, επαναχρησιμοποιούμενα και μεταλλαγμένα με την πάροδο του χρόνου, και αυτός είναι ο λόγος για τον οποίο η νοοτροπία Σύνθεσης είναι τόσο φυσική στο Flutter.
Στο εσωτερικό, η διάταξη και στα δύο πλαίσια ακολουθεί το ίδιο μοτίβο που βασίζεται σε περιορισμούς και καθοδηγείται από γονείς. Ο γονέας μετράει τον εαυτό του, μεταβιβάζει περιορισμούς, τα παιδιά επιλέγουν ένα μέγεθος που σέβεται αυτούς τους περιορισμούς και ο γονέας τοποθετεί τα παιδιά του. Στο Flutter θα δείτε αυτό να εμφανίζεται απευθείας ως BoxConstraintsΣτο Compose, η διαχείριση γίνεται σε εφαρμογές MeasurePolicy. Και στις δύο περιπτώσεις, οι γονείς μπορούν να περιορίσουν τα παιδιά—τα γραφικά στοιχεία δεν μπορούν απλώς να επιλέξουν οποιοδήποτε μέγεθος ή θέση θέλουν.
Δόμηση μιας εφαρμογής: σημείο εισόδου, σκαλωσιά και διατάξεις
Σε Android με Compose, το σημείο εισόδου σας είναι συνήθως ένα Activity (συχνά ένα ComponentActivity) όπου καλείτε setContent για να φιλοξενήσετε τα σύνθετα αρχεία σας. Από εκεί δημιουργείτε το συνθέσιμο δέντρο, ξεκινώντας συνήθως με ένα MaterialTheme και μια επιφάνεια ή σκαλωσιά που καθορίζει τη διάταξη υψηλού επιπέδου.
Στο Flutter, το σημείο εισόδου είναι ένα Dart main συνάρτηση που καλεί runApp με το root widget της εφαρμογής σας. Αυτή η ρίζα είναι συνήθως μια MaterialApp or WidgetsApp widget, το οποίο ρυθμίζει τη δρομολόγηση, τη θεματοποίηση, την τοπική προσαρμογή και τον βασικό πλοηγό. Η πρώτη «οθόνη» που εμφανίζετε συχνά χρησιμοποιεί ένα Scaffold widget, το οποίο παίζει έναν ρόλο πολύ παρόμοιο με Scaffold στο Material 3 Compose: σας δίνει γραμμή εφαρμογής, σώμα, κινητό κουμπί δράσης, συρτάρια και ούτω καθεξής.
Για απλό κείμενο και στατικό περιεχόμενο, το Compose ενδέχεται να έχει ως προεπιλογή την αναδίπλωση περιεχομένου—αντιστοιχίζοντας το μέγεθος με το εγγενές περιεχόμενο—ενώ πολλά γραφικά στοιχεία Flutter έχουν ως προεπιλογή την κατάληψη περισσότερου διαθέσιμου χώρου, εκτός εάν υπάρχει περιορισμός. Για παράδειγμα, αν τοποθετήσετε ένα Text μπορεί να συντεθεί μέσα σε μια στήλη, δεν θα γεμίσει αυτόματα το πλάτος. Στο Flutter, ένα Text μέσα σε ένα Column μπορεί να συμπεριφέρεται διαφορετικά ανάλογα με τους περιορισμούς του γονικού στοιχείου. Για να κεντράρετε περιεχόμενο στο Flutter, συχνά θα τυλίγετε τα πράγματα σε ένα Center γραφικό στοιχείο ή χρησιμοποιήστε γραφικά στοιχεία διάταξης όπως Align, Row, Columnκαι Expanded σε συνδυασμό με ιδιότητες ευθυγράμμισης.
Οι γραμμικές διατάξεις αντιστοιχίζονται σχεδόν τέλεια: Το Compose έχει Row Column, και το ίδιο κάνει και το Flutter. Στο Flutter προσπερνάς τα παιδιά ως List<Widget> και ελέγχει την απόσταση και την ευθυγράμμιση με ιδιότητες όπως MainAxisAlignment CrossAxisAlignmentΣτο Compose, βασίζεστε σε horizontalArrangement, verticalArrangement, horizontalAlignment verticalAlignmentΈνας χρήσιμος τρόπος για να το σκεφτείτε: οι ιδιότητες που τελειώνουν σε «Διάταξη» αντιστοιχίζονται στον κύριο άξονα του Flutter, και αυτές που τελειώνουν σε «Στοίχιση» αντιστοιχίζονται στον εγκάρσιο άξονα.
Όταν χρειάζεστε σχετικές ή επικαλυπτόμενες διατάξεις, οι προσεγγίσεις είναι επίσης εννοιολογικά ευθυγραμμισμένες. Στο Android XML μπορείτε να βρείτε RelativeLayout ή ένα ένθετο μείγμα από LinearLayout FrameLayoutΣτο Compose θα συνθέσετε Row, Column Box (ή γράψτε μια προσαρμοσμένη διάταξη). Στο Flutter το αναλογικό είναι Row, Column Stack σε συνδυασμό με τοποθετημένα παιδιά και επιλογές ευθυγράμμισης. Το νοητικό σας μοντέλο για την τακτοποίηση στοιχείων σε σχέση μεταξύ τους κινείται σχεδόν αμετάβλητο.
Κουμπιά, εισαγωγή και αλληλεπίδραση
Στο Jetpack Compose, η κατασκευή ενός κουμπιού συνήθως σημαίνει χρήση Button ή μία από τις παραλλαγές του Υλικού του, η οποία στο Υλικό 3 επιλύεται σε μια συγκεκριμένη υλοποίηση όπως FilledTonalButton. Παρέχετε ένα onClick λάμδα και προαιρετικό στυλ, συχνά μέσω παραμέτρων όπως colors ή τροποποιητές για συμπλήρωση, πλάτος και ευθυγράμμιση.
Στο Flutter, το αντίστοιχο είναι η χρήση widgets όπως FilledButton, ElevatedButton, TextButton or OutlinedButton. Κάθε ένα παίρνει ένα onPressed επανάκληση και ένα child widget—συνήθως ένα TextΜπορείτε να τα προσαρμόσετε περνώντας ένα style μέσω ButtonStyle ή χρησιμοποιώντας μια καθολική παράκαμψη θέματος, η οποία σας επιτρέπει να προσαρμόζετε κεντρικά το χρώμα, το σχήμα, το ύψος και το μέγεθος για μια ολόκληρη οικογένεια κουμπιών.
Για τον χειρισμό χειρονομιών, το Compose βασίζεται σε τροποποιητές όπως Modifier.clickable σε πολλές περιπτώσεις, αλλά μπορείτε επίσης να κατευθυνθείτε σε εξειδικευμένους ανιχνευτές χειρονομιών όταν χρειάζεται. Τα παρατεταμένα πατήματα, τα σύρματα και οι προσαρμοσμένες χειρονομίες συνήθως συντίθενται μέσω ειδικών API τροποποίησης και πηγών αλληλεπίδρασης.
Το Flutter αποκαλύπτει μια σαφή GestureDetector ένα γραφικό στοιχείο που περιβάλλει οτιδήποτε δεν διαθέτει ενσωματωμένη υποστήριξη χειρονομιών. Προσφέρει ένα ευρύ φάσμα επανακλήσεων: onTap, onDoubleTap, onLongPress, onVerticalDragStart, onVerticalDragUpdate, onHorizontalDragEnd και πολλά άλλα. Widgets όπως ElevatedButton ήδη εκθέτει ένα onPressed ιδιότητα, αλλά για πλήρως προσαρμοσμένα στοιχεία UI μπορείτε να χρησιμοποιήσετε GestureDetector ή γραφικά στοιχεία υψηλότερου επιπέδου, όπως InkWell για την ανατροφοδότηση κυματισμού υλικού.
Η εισαγωγή κειμένου στο Flutter διαχειρίζεται με TextField or TextFormField, του οποίου το στυλ είναι παράλληλο με αυτό του Compose TextField OutlinedTextField σύνθετα. Μπορείτε να διαμορφώσετε υποδείξεις, ετικέτες, σφάλματα και περιγράμματα χρησιμοποιώντας ένα InputDecoration παρόμοιο με τον τρόπο που χρησιμοποιείτε TextFieldDefaults ή παραμέτρους στα πεδία κειμένου Compose. Όπως και στο Compose, συνήθως εμφανίζετε μηνύματα σφάλματος αντιδραστικά αλλάζοντας την κατάσταση και αναδημιουργώντας τη διακόσμηση αντί να χειρίζεστε χειροκίνητα τις προβολές.
Λίστες, πλέγματα και περιεχόμενο με δυνατότητα κύλισης
Το Jetpack Compose προσφέρει δύο κύριες στρατηγικές για λίστες: απλή Column/Row με επανάληψη για μικρές συλλογές, και LazyColumn/LazyRow/LazyVerticalGrid/LazyHorizontalGrid για μεγάλες ή δυναμικές λίστες. Τα αδρανή κοντέινερ συνθέτουν μόνο ό,τι είναι ορατό, γεγονός που διατηρεί την απόδοση υψηλή κατά την επεξεργασία χιλιάδων στοιχείων.
Το Flutter ακολουθεί την ίδια προσέγγιση small‑vs‑large, αλλά με διαφορετικά widgets. Για μια μικρή λίστα που χωράει στην οθόνη, θα μπορούσατε απλώς να χρησιμοποιήσετε ένα Column και αντιστοιχίστε τα δεδομένα σας σε childrenΓια οτιδήποτε κυλάει, ψάχνεις για ListView or GridView, με κατασκευαστές-κατασκευαστές που δημιουργούν νωχελικά παιδιά μόνο όταν χρειάζεται.
Το κοινό μοτίβο στο Flutter είναι ListView.builder, το οποίο αντικατοπτρίζει τα στοιχεία λίστας lazy DSL του Compose. Παρέχετε ένα itemCount και ένα itemBuilder επανάκληση; Το Flutter καλεί αυτόν τον κατασκευαστή με δείκτη από 0 έως itemCount - 1 κάθε φορά που εμφανίζεται ένα νέο στοιχείο. Μέσα στο εργαλείο δημιουργίας μπορείτε να επιστρέψετε σχεδόν οποιοδήποτε γραφικό στοιχείο—από ένα απλό ListTile με κείμενο και εικονίδιο σε σύνθετες, προσαρμοσμένες γραμμές λίστας.
Για πλέγματα, το Compose's LazyVerticalGrid LazyHorizontalGrid χάρτης προς Flutter's GridView widget Αντί να μεταβιβάζει τις μετρήσεις στηλών απευθείας στο πλέγμα, το Flutter χρησιμοποιεί συχνά έναν εκπρόσωπο όπως SliverGridDelegateWithFixedCrossAxisCount or SliverGridDelegateWithMaxCrossAxisExtent για να ελέγχουν τον τρόπο διάταξης των κελιών. Αυτοί οι αντιπρόσωποι ενσωματώνουν κανόνες όπως «αριθμός στηλών» ή «μέγιστο πλάτος κελιού», παρόμοιοι σε πνεύμα με τις παραμέτρους μεγέθους πλέγματος που χρησιμοποιείτε στο Compose.
Η συμπεριφορά κύλισης είναι επίσης ανάλογη και στα δύο κιτ εργαλείων. Οι λίστες lazy του Compose διαθέτουν ενσωματωμένη δυνατότητα κύλισης. Δεν τις τυλίγετε σε επιπλέον κοντέινερ κύλισης. Στο Flutter, πολλά γραφικά στοιχεία λίστας και πλέγματος είναι τα ίδια με δυνατότητα κύλισης, αλλά για μεμονωμένο, μη επαναλαμβανόμενο περιεχόμενο που θα πρέπει να κάνει κύλιση, μπορείτε να χρησιμοποιήσετε SingleChildScrollViewΗ δημιουργία προσαρμοσμένων σελίδων με δυνατότητα κύλισης γίνεται στη συνέχεια θέμα ένθεσης ή σύνθεσης slivers για πιο προηγμένες περιπτώσεις χρήσης.
Προσαρμοστικά και responsive μοτίβα UI
Το Compose σας προσφέρει αρκετές στρατηγικές για responsive design: προσαρμοσμένες διατάξεις, BoxWithConstraints, WindowSizeClass και την προσαρμοστική βιβλιοθήκη Material 3. Αυτά σας επιτρέπουν να αλλάζετε τη σύνθεσή σας με βάση τον διαθέσιμο χώρο, τη στάση του σώματος και την κατηγορία της συσκευής, και μπορείτε να τα συνδυάσετε ανάλογα με την πολυπλοκότητα του έργου.
Το Flutter δεν προσπαθεί να αντικατοπτρίσει άμεσα αυτά τα API, αλλά η υποκείμενη ιδέα είναι η ίδια: ελέγξτε τους περιορισμούς και τα χαρακτηριστικά της οθόνης και, στη συνέχεια, διακλαδώστε τη διάταξή σας. Τα δύο κύρια εργαλεία είναι LayoutBuilder MediaQuery. LayoutBuilder περάσματα BoxConstraints προς τα κάτω, ώστε να μπορείτε να εναλλάσσετε ή να αναδιατάσσετε τα γραφικά στοιχεία πάνω από συγκεκριμένα πλάτη ή ύψη. MediaQuery Εκθέτει το μέγεθος της οθόνης, τον προσανατολισμό, την αναπλήρωση και την πυκνότητα των pixel για σημεία διακοπής υψηλού επιπέδου.
Αντί να στοχεύετε σε μια αντιστοίχιση ένα προς ένα μεταξύ των προσαρμοστικών λύσεων του Compose και του Flutter, είναι πιο αποτελεσματικό να σκεφτείτε με βάση τις απαιτήσεις σχεδιασμού σας. Μόλις μάθετε πώς θα πρέπει να προσαρμόζεται το UI σας σε τηλέφωνα, tablet και επιτραπέζιους υπολογιστές, μπορείτε να εκφράσετε αυτήν τη λογική είτε μέσω του Compose's WindowSizeClass και προσαρμοστικές διατάξεις ή η διακλάδωση που βασίζεται σε περιορισμούς και μέσα του Flutter. Ίδια σχεδιαστική σκέψη—διαφορετικά API.
Διαχείριση κατάστασης: θυμηθείτε vs StatefulWidget και όχι μόνο
Το Jetpack Compose αποθηκεύει την προσωρινή κατάσταση του UI χρησιμοποιώντας remember και κρατικοί κάτοχοι όπως mutableStateOf, συχνά σε συνδυασμό με ViewModel και αρχιτεκτονικά στοιχεία για μακροβιότερη κατάσταση. Όταν η κατάσταση αλλάζει, συμβαίνει ανασύνθεση και τα σχετικά συνθέσιμα στοιχεία λαμβάνουν νέες τιμές.
Η ιστορία χαμηλού επιπέδου του Flutter περιστρέφεται γύρω από StatefulWidget και το σχετικό State αντικείμενο. Ορίζετε ένα γραφικό στοιχείο που θέλει να διατηρήσει την κατάσταση επεκτείνοντας StatefulWidget, στη συνέχεια εφαρμόστε ένα ξεχωριστό State<MyWidget> κλάση για την αποθήκευση μεταβλητών πεδίων. Κάθε φορά που ενημερώνετε αυτά τα πεδία, καλείτε setState(), το οποίο επισημαίνει αυτό το τμήμα του δέντρου γραφικών στοιχείων ως βρώμικο και ενεργοποιεί μια αναδόμηση. Σε αυτό το επίπεδο είναι πολύ παρόμοιο με την αποθήκευση της κατάστασης Σύνταξης με remember και ακύρωση των συνθέσιμων στοιχείων όταν αλλάζουν οι τιμές.
Για πιο σύνθετες εφαρμογές, το Flutter βασίζεται σε μεγάλο βαθμό σε μοτίβα κοινότητας και πρώτου κατασκευαστή: Provider, καταστήματα τύπου Riverpod, Bloc, Redux και άλλα. Αυτά λειτουργούν ως ανάλογα των στοίβων αρχιτεκτονικής Android: ViewModel + LiveData/Flow + αποθετήρια σε έργα Compose. Συγκεντρώνουν την επιχειρηματική λογική και εκθέτουν αντιδραστικές ροές δεδομένων που οδηγούν σε ανακατασκευές widget. Από ένα υπόβαθρο Compose, θα διαπιστώσετε ότι πολλά από αυτά τα μοτίβα είναι οικεία, ακόμη και αν τα API διαφέρουν.
Ένα σημείο που συχνά εκπλήσσει τους προγραμματιστές Android είναι ότι τόσο τα widgets χωρίς κατάσταση όσο και τα widgets με κατάσταση στο Flutter ανακατασκευάζονται συχνά—πιθανώς κάθε καρέ κατά τη διάρκεια των κινούμενων εικόνων. Η διάκριση δεν αφορά τη συχνότητα ανακατασκευής αλλά το πού αποθηκεύεται η μεταβλητή κατάσταση: StatefulWidget σου δίνει μια συντροφιά State αντικείμενο που επιβιώνει από τις ανακατασκευές, όπως ακριβώς και το πώς remember επιτρέπει στις τιμές να επιβιώσουν από την ανασύνθεση στο Compose.
Σχέδιο, κινούμενα σχέδια και οπτική βελτίωση
Αν έχετε εργαστεί ποτέ απευθείας με Android Canvas Drawable, Compose's Canvas Το συνθέσιμο πιθανότατα έμοιαζε απλό. Παρέχει έναν δηλωτικό τρόπο σχεδίασης σχημάτων, εικόνων και κειμένου στο Kotlin, αποκρύπτοντας μεγάλο μέρος της επιτακτικής τελετής των παραδοσιακών προσαρμοσμένων προβολών.
Το Flutter εκθέτει μια παρόμοια επιφάνεια σχεδίασης μέσω του Canvas API, προσβάσιμο μέσω CustomPaint CustomPainter. Εφαρμόζετε ένα CustomPainter κλάση όπου παρακάμπτετε την paint μέθοδος σχεδίασης στον καμβά χρησιμοποιώντας Paint αντικείμενα, διαδρομές, μετασχηματισμούς και ούτω καθεξής. Στη συνέχεια, συνδέετε αυτόν τον ζωγράφο σε ένα CustomPaint widget. Στο εσωτερικό, τόσο το Compose όσο και το Flutter βασίζονται στη μηχανή Skia, επομένως τα πρωτόγονα στοιχεία—γραμμές, διαδρομές, shaders—μοιάζουν πολύ οικεία από την δισδιάστατη απόδοση του Android.
Για τα κινούμενα σχέδια, το Flutter βασίζεται σε ένα σαφές σύστημα κινούμενων σχεδίων που έχει σχεδιαστεί γύρω από AnimationController, Animation<T> και Tweens, καθώς και ένα πλούσιο σύνολο κινούμενων widgets. Δημιουργείτε ένα στιγμιότυπο ενός ελεγκτή (συνήθως με SingleTickerProviderStateMixin για vsync), ορίστε CurvedAnimations ή Tweens που αντιστοιχίζουν την πρόοδο 0-1 σε τιμές τομέα και, στη συνέχεια, συνδέστε τα σε γραφικά στοιχεία όπως FadeTransition, ScaleTransition, AnimatedBuilder ή έμμεσα γραφικά στοιχεία όπως AnimatedContainerΤο σύστημα κινουμένων σχεδίων εκθέτει επίσης AnimationStatus επανακλήσεις για αντίδραση στην έναρξη, την ολοκλήρωση ή την αντιστροφή.
Τα API κινουμένων σχεδίων του Jetpack Compose είναι δηλωτικά από πάνω προς τα κάτω, με συναρτήσεις όπως animate*AsState, μεταβάσεις και κινούμενη ορατότητα. Αντί να διαχειρίζεστε τους ελεγκτές χειροκίνητα στις περισσότερες συνηθισμένες περιπτώσεις, περιγράφετε τις καταστάσεις-στόχους και το πλαίσιο ελέγχει την παρεμβολή με την πάροδο του χρόνου. Όταν χρειάζεστε περισσότερο εξατομικευμένο έλεγχο, εξακολουθείτε να έχετε πρόσβαση σε αρχέγονα στοιχεία χαμηλού επιπέδου, αλλά η συνήθης διαδρομή είναι πιο συνοπτική από την κλασική XML Android ή τον κώδικα επιτακτικής κίνησης.
Εννοιολογικά, χρησιμοποιείτε και τα δύο κιτ εργαλείων με τον ίδιο τρόπο: διατηρείτε τα γραφικά στοιχεία/τα σύνθετα στοιχεία ελαφριά και καθαρά, προωθείτε χρονικά μεταβαλλόμενες τιμές μέσω αυτών και αφήνετε το πλαίσιο να χειρίζεται την παρεμβολή και την ακύρωση. Ως προγραμματιστής του Compose, η επιπλέον σαφήνεια του Flutter AnimationController Μπορεί στην αρχή να σας φαίνεται λίγο παλιομοδίτικο, αλλά σας προσφέρει πολύ λεπτομερή έλεγχο του χρονισμού, των καμπυλών και της ενορχήστρωσης.
Στυλ, θεματοποίηση, γραμματοσειρές και στοιχεία
Οι σύγχρονες εφαρμογές είτε ζουν είτε πεθαίνουν με την τελειοποίηση, επομένως τόσο το Flutter όσο και το Compose δίνουν μεγάλη έμφαση στη θεματολογία και το στυλ. Χρήσεις σύνθεσης MaterialTheme με χρωματικά σχήματα, τυπογραφία και ορισμούς σχημάτων, και μπορείτε να ενσωματώσετε θέματα για να παρακάμψετε τιμές για δευτερεύοντα δέντρα—συμπεριλαμβανομένης της επιβολής ανοιχτόχρωμων ή σκοτεινών επιφανειών για συγκεκριμένες περιοχές.
Στο Flutter, το αντίστοιχο είναι ThemeData πέρασε σε MaterialApp or Theme γραφικά στοιχεία. Ορίζετε τα βασικά χρώματα, τη φωτεινότητα, την τυπογραφία και θέματα ειδικά για τα στοιχεία, όπως elevatedButtonTheme, textButtonTheme, appBarTheme και πολλά άλλα. Μπορείτε να παρακάμψετε τα θέματα τοπικά περικλείοντας υποδέντρα σε Theme widgets που αντιγράφουν το γονικό στοιχείο και τροποποιούν ορισμένα πεδία. Η φωτεινή και η σκοτεινή λειτουργία μπορούν να εναλλάσσονται σε επίπεδο εφαρμογής παρέχοντας theme darkTheme και τον έλεγχο themeMode.
Το στυλ κειμένου είναι οικείο: στο Compose είτε μεταβιβάζετε απλές ιδιότητες απευθείας στο Text ή να παρέχει ένα TextStyle αντικείμενο. Το Flutter αντικατοπτρίζει αυτό με ένα Text γραφικό στοιχείο που δέχεται ένα TextStyle μέσω του style παράμετρος. TextStyle καλύπτει την οικογένεια γραμματοσειρών, το μέγεθος, το πάχος, την απόσταση γραμμάτων, το ύψος γραμμών, τη διακόσμηση και άλλα. Μπορείτε να ορίσετε καθολικά θέματα κειμένου στο ThemeData.textTheme και να τα αναφέρετε παντού, όπως ακριβώς θα χρησιμοποιούσατε την τυπογραφία από MaterialTheme στο Compose.
Οι γραμματοσειρές και οι εικόνες διαχειρίζονται μέσω στοιχείων αντί για το παραδοσιακό Android. /res δέντρο καταλόγου. Το Flutter δεν επιβάλλει συγκεκριμένη διάταξη φακέλου. Δηλώνετε περιουσιακά στοιχεία σε pubspec.yaml και στη συνέχεια να τα αναφέρετε από τον κώδικα. Οι εικόνες συνήθως φορτώνονται με Image.asset(), η οποία αναλύεται στον σωστό κάδο πυκνότητας με βάση devicePixelRatioΤα λογικά εικονοστοιχεία παίζουν τον ίδιο ρόλο με τα dp σε Android, αφαιρώντας την φυσική πυκνότητα των pixel.
Για προσαρμοσμένες γραμματοσειρές, το Compose σάς επιτρέπει είτε να συσκευάζετε πόρους γραμματοσειρών είτε να τους ανακτάτε κατά τον χρόνο εκτέλεσης μέσω παρόχων όπως οι Google Fonts και, στη συνέχεια, να τους συνδέετε σε FontFamily και τυπογραφία. Το Flutter χρησιμοποιεί σχεδόν το ίδιο μοτίβο: τοποθετήστε τα αρχεία γραμματοσειρών σε έναν φάκελο assets, καταχωρίστε τα σε pubspec.yamlκαι, στη συνέχεια, αναφέρετε την οικογένεια γραμματοσειρών με βάση το όνομα στο TextStyleΑν θέλετε γραμματοσειρές που ανακτώνται κατά το χρόνο εκτέλεσης, υπάρχει μια δημοφιλής google_fonts πρόσθετο που εκθέτει συναρτήσεις Dart που έχουν ονομαστεί από γραμματοσειρές—π.χ. GoogleFonts.robotoTextTheme()—για να τα ενσωματώσετε γρήγορα στο θέμα σας.
Και τα δύο οικοσυστήματα αντιμετωπίζουν τις συμβολοσειρές και την τοπική προσαρμογή ως ζητήματα πρώτης κατηγορίας, αν και το Flutter δεν διαθέτει άμεσο ισοδύναμο με τους πόρους συμβολοσειρών XML του Android. Αντίθετα, η καλύτερη πρακτική είναι να διατηρούνται οι μεταφράσεις .arb αρχεία και να τα συνδέσετε με την αλυσίδα εργαλείων εντοπισμού Flutter. Η πρόσβαση γίνεται στη συνέχεια μέσω δημιουργημένων κλάσεων Dart, περίπου ανάλογη με τη χρήση R.string αναγνωριστικά στον κώδικα Android.
Έννοιες πλατφόρμας Android μέσα από το πρίσμα του Flutter
Πέρα από το UI, ένα από τα μεγαλύτερα ερωτήματα που έχουν οι προγραμματιστές του Compose είναι το πώς οι γνώσεις τους για το Android αντιστοιχίζονται με την αρχιτεκτονική του Flutter. Ευτυχώς, πολλές από τις βασικές ιδέες - δραστηριότητες, κύκλος ζωής, προθέσεις, εργασία υποβάθρου, πόροι, δικτύωση - έχουν σαφή αντίστοιχα, ακόμα κι αν το API επιφάνειας φαίνεται διαφορετικό.
Στο Android, Activity Fragment είναι οι κύριες οθόνες και τα κοντέινερ σας. Στο Flutter όλα είναι ένα γραφικό στοιχείο και η πλοήγηση γίνεται μέσω Navigator Route αντικείμενα. Μια διαδρομή αντιστοιχεί περίπου σε μια δραστηριότητα ή τμήμα, αλλά συνήθως υπάρχει μόνο μία φιλοξενία. Activity σε Android που ενσωματώνει τη μηχανή Flutter. Προωθείτε και εμφανίζετε διαδρομές στη στοίβα του Navigator, είτε μέσω επώνυμων διαδρομών που ορίζονται στο MaterialApp ή μέσω απευθείας κατασκευασμένων PageRoute περιπτώσεις όπως MaterialPageRoute.
Επανακλήσεις κύκλου ζωής του Android (onCreate, onStart, onResume, κ.λπ.) δεν έχουν άγκιστρα ένα προς ένα στον κώδικα Flutter, αλλά μπορείτε να παρατηρήσετε τον κύκλο ζωής της εφαρμογής με WidgetsBindingObserver. Εκθέτει πολιτείες όπως resumed, inactive, paused detached, τα οποία αντιστοιχούν περίπου στις ορατές, τις φάσεις φόντου και τις φάσεις που έχουν καταστραφεί του Android. Όταν χρειάζεστε πραγματικά hooks κύκλου ζωής χαμηλού επιπέδου για τη διαχείριση πόρων, συνήθως τα εφαρμόζετε στην εγγενή πλευρά του Android στο FlutterActivity ή ένα πρόσθετο, όχι στο Dart.
Τα intents παίζουν δύο ρόλους στο Android: πλοήγηση εντός εφαρμογής και επικοινωνία μεταξύ εφαρμογών. Όπως αναφέρθηκε, το Flutter δεν διαθέτει API πλοήγησης βασισμένο σε intent — το Navigator το αντικαθιστά πλήρως στον κόσμο του Dart. Για εργασίες μεταξύ εφαρμογών (εκκίνηση κάμερας, επιλογή αρχείων, χειρισμός intents κοινής χρήσης), γενικά χρησιμοποιείτε πρόσθετα που τυλίγουν τις απαραίτητες κλήσεις Android (και iOS). Εάν δεν υπάρχει πρόσθετο, μπορείτε να γράψετε το δικό σας χρησιμοποιώντας το MethodChannels για να επικοινωνείτε μεταξύ του Dart και του εγγενούς κώδικα, προωθώντας τα intents και τα αποτελέσματα ως μηνύματα.
Η κατανόησή σας για την εργασία στο παρασκήνιο και την ανάπτυξη νημάτων μεταφέρεται επίσης, αλλά τα πρωτόγονα στοιχεία φαίνονται διαφορετικά. Το Android σάς ωθεί να μετακινήσετε τις εισόδους/εξόδους δικτύου και δίσκου από το κύριο νήμα χρησιμοποιώντας coroutines, AsyncTask (παλαιού τύπου), WorkManager, JobScheduler, RxJava και ούτω καθεξής. Το Dart, αντίθετα, χρησιμοποιεί έναν βρόχο συμβάντων ενός νήματος ανά isolate, με async/await για είσοδο/εξόδου και ξεχωριστά isolates για εργασία με μεγάλο όγκο CPU. Για οτιδήποτε συνδέεται με είσοδο/εξόδου, απλώς επισημάνετε τις συναρτήσεις σας. async, await τη λειτουργία και αφήστε τον βρόχο συμβάντων να διατηρεί το περιβάλλον χρήστη σε απόκριση. Για βαριές εργασίες CPU, δημιουργείτε ένα isolate και επικοινωνείτε μέσω μετάδοσης μηνυμάτων αντί για κοινόχρηστη μνήμη.
Στο μέτωπο της δικτύωσης, το δημοφιλές Flutter http Το πακέτο παίζει παρόμοιο ρόλο με το OkHttp + Retrofit για βασικές περιπτώσεις χρήσης. Κρύβει μεγάλο μέρος της εργασίας socket χαμηλού επιπέδου και ενσωματώνεται φυσικά με το async/await. Για σύνθετες ανάγκες μπορείτε να χρησιμοποιήσετε πακέτα όπως dio, αλλά το βασικό μοτίβο παραμένει: πραγματοποιήστε μια ασύγχρονη κλήση, περιμένετε το αποτέλεσμα, ενημερώστε την κατάσταση με setState() ή τον διαχειριστή κατάστασης της επιλογής σας και ανακατασκευάστε τα επηρεαζόμενα γραφικά στοιχεία.
Πρόσθετα, αποθηκευτικός χώρος, Firebase και εργαλεία
Στο Android έχετε συνηθίσει να δηλώνετε εξαρτήσεις στο Gradle. Στο Flutter τις δηλώνετε στο pubspec.yaml και να τα ανακτήσετε από το pub.dev. Τα αρχεία Gradle κάτω από το android/ Ο φάκελος ενός έργου Flutter προορίζεται κυρίως για ενσωματώσεις συγκεκριμένης πλατφόρμας ή όταν χρειάζεστε προσαρμοσμένες εγγενείς βιβλιοθήκες — η καθημερινή ανάπτυξη εφαρμογών παραμένει στη χώρα του Dart.
Οι κοινόχρηστες προτιμήσεις και το SQLite έχουν επίσης έτοιμα ισοδύναμα. Πού προσφέρει το Android SharedPreferences για μικρό χώρο αποθήκευσης κλειδιών-τιμών και SQLite (ή Room) για δομημένα δεδομένα, το Flutter τα τυλίγει μέσω πρόσθετων όπως shared_preferences sqfliteΑυτά τα πρόσθετα ενοποιούν τη συμπεριφορά Android και iOS, ώστε να μπορείτε να χρησιμοποιείτε ένα μόνο Dart API ανεξάρτητα από την πλατφόρμα, ενώ παράλληλα να βασίζεστε στις υποκείμενες εγγενείς υλοποιήσεις.
Η ενσωμάτωση του Firebase είναι εξίσου απλή και άριστης ποιότητας. Οι περισσότερες υπηρεσίες Firebase—Έλεγχος ταυτότητας, Firestore, Βάση δεδομένων πραγματικού χρόνου, ανταλλαγή μηνυμάτων cloud, Analytics, απομακρυσμένη διαμόρφωση και άλλες—έχουν επίσημα πρόσθετα Flutter που συντηρούνται από τις ομάδες Firebase και Flutter. Αντικατοπτρίζουν το εννοιολογικό μοντέλο από τα SDK Firebase του Android, αλλά με Dart‑idiomatic API. Για περισσότερες εξειδικευμένες λειτουργίες Firebase που δεν καλύπτονται άμεσα, υπάρχει ένα υγιές οικοσύστημα προσθέτων τρίτων στο pub.dev.
Για τον εντοπισμό σφαλμάτων και τη δημιουργία προφίλ, η σουίτα DevTools του Flutter σάς προσφέρει μια πλούσια εργαλειοθήκη άμεσα συγκρίσιμη με το profiler και το Layout Inspector του Android Studio. Μπορείτε να ελέγξετε το δέντρο των widget, να παρακολουθήσετε τις ανακατασκευές, να παρακολουθήσετε τις κατανομές μνήμης, να διαγνώσετε διαρροές και κατακερματισμό και να αναλύσετε τον κώδικα Dart. Σε συνδυασμό με την υποστήριξη IDE στο Android Studio και το VS Code, την άμεση επαναφόρτωση και την άμεση επανεκκίνηση, ο κύκλος ανατροφοδότησης στην ανάπτυξη του Flutter είναι τουλάχιστον εξίσου αυστηρός - και συχνά πιο αυστηρός - από αυτόν που έχετε συνηθίσει με το Compose.
Οι ειδοποιήσεις push, μια άλλη συνηθισμένη ανησυχία για το Android, αντιμετωπίζονται στο Flutter μέσω πρόσθετων όπως firebase_messaging. Στο εσωτερικό, αυτά επικοινωνούν με το Firebase Cloud Messaging και τα εγγενή πλαίσια ειδοποιήσεων σε Android και iOS, αλλά η λογική της εφαρμογής σας βρίσκεται σε ένα ενοποιημένο Dart API. Η διαμόρφωση και οι συμπεριφορές που αφορούν συγκεκριμένες πλατφόρμες (όπως τα κανάλια ειδοποιήσεων στο Android) εξακολουθούν να είναι σημαντικές και η υπάρχουσα εμπειρία σας με αυτές τις λεπτομέρειες της πλατφόρμας εξακολουθεί να είναι εξαιρετικά σχετική.
Ακόμα και τα γραφικά στοιχεία της αρχικής οθόνης στο Android, τα οποία δεν μπορούν να υλοποιηθούν αποκλειστικά στο Flutter, μπορούν να ενσωματωθούν με τον κώδικα του Flutter. Συνήθως τα δημιουργείτε με διατάξεις Jetpack Glance ή XML και στη συνέχεια χρησιμοποιείτε ένα πακέτο όπως home_widget για να επικοινωνείτε με την εφαρμογή Flutter, να μοιράζεστε δεδομένα, ακόμη και να ενσωματώνετε ραστεροποιημένο περιβάλλον χρήστη Flutter ως εικόνα μέσα στο εγγενές γραφικό στοιχείο. Αυτή η υβριδική προσέγγιση σάς επιτρέπει να διατηρήσετε την κύρια εμπειρία σας στο Flutter, τηρώντας παράλληλα τους περιορισμούς της πλατφόρμας.
Κοιτάζοντας όλες αυτές τις ομοιότητες, ένας προγραμματιστής του Jetpack Compose που εισέρχεται στο Flutter δεν ξεκινά καθόλου από το μηδέν. Η κατανόησή σας για το δηλωτικό περιβάλλον χρήστη, τον κύκλο ζωής του Android, την πλοήγηση, την κατάσταση, τους πόρους και την ασύγχρονη εργασία ταιριάζει πολύ φυσικά στον κόσμο του Flutter. Αυτό που αλλάζει περισσότερο είναι τα ονόματα, η γλώσσα (Dart) και η νοοτροπία πολλαπλών πλατφορμών. Μόλις ενσωματώσετε τα widgets και το Navigator ως βασικές έννοιες, η υπόλοιπη στοίβα τείνει να κουμπώνει στη θέση της αρκετά γρήγορα.