- Flutter i Jetpack Compose mają wspólny deklaratywny, reaktywny model interfejsu użytkownika, ale różnią się językiem, ekosystemem i zasięgiem platformy.
- Twórz mapy w sposób przejrzysty zgodnie z koncepcjami Flutter: elementy kompozycyjne do widżetów, listy Lazy do ListView/GridView, Canvas do CustomPainter, a motywy do ThemeData.
- Umiejętności natywne systemu Android (cykl życia, nawigacja, zasoby, współbieżność) są przenoszone bezpośrednio do Fluttera poprzez widżety, Nawigator, zasoby i polecenia async/await.
- Compose sprawdza się w projektach przeznaczonych wyłącznie na system Android, natomiast Flutter sprawdza się znakomicie, gdy potrzebujesz pojedynczej bazy kodu dla systemów Android, iOS, stron internetowych i komputerów stacjonarnych.
Jeśli czujesz się już pewnie, pisząc interfejsy użytkownika za pomocą Jetpack Compose i zastanawiasz się, jak trudno jest przejść na Flutter, to jesteś w świetnej sytuacji. Oba zestawy narzędzi są deklaratywne, reaktywne i stworzone przez Google, więc ogromna część Twojego modelu mentalnego jest przenoszona niemal w stosunku jeden do jednego. Główne różnice tkwią w języku (Kotlin vs Dart), strukturze projektu oraz sposobie, w jaki każdy framework komunikuje się z bazowymi warstwami Androida (a w przypadku Fluttera również iOS, web i desktop).
Niniejszy przewodnik został napisany specjalnie dla programistów Jetpack Compose, którzy chcą dogłębnie zrozumieć Flutter, bez zbędnych marketingowych dodatków. Zobaczysz, jak podstawowe koncepcje mapują się między tymi dwoma światami: obiekty kompozycyjne kontra widżety, modyfikatory kontra parametry konstruktora, układy Lazy kontra ListView/GridView, Canvas kontra CustomPainter, Navigation Compose kontra Navigator, Remember kontra StatefulWidget i wiele więcej. Połączymy również Twoje szersze doświadczenie w Androidzie (widoki, cykl życia, zasoby, intencje, działanie w tle) z ich odpowiednikami w Flutterze, dzięki czemu nauka będzie bardziej przypominała chodzenie bokiem niż wspinaczkę.
Z Jetpack Compose do Fluttera: gdzie przenieść swoje umiejętności
Flutter to środowisko interfejsu użytkownika firmy Google służące do tworzenia aplikacji wieloplatformowych przy użyciu języka Dart, natomiast Jetpack Compose to nowoczesny zestaw narzędzi interfejsu użytkownika firmy Google przeznaczony dla natywnego systemu Android, który korzysta z języka Kotlin. Pod maską każdy z nich jest przeznaczony dla innego środowiska wykonawczego, ale pod względem architektonicznym łączy je ta sama główna idea: opisanie interfejsu użytkownika jako funkcji stanu i pozwolenie frameworkowi na ustalenie, kiedy i jak go przerysować.
W Jetpack Compose myślisz w kategoriach funkcji komponowalnych, modyfikatorów i rekompozycji; w Flutter myślisz w kategoriach widżetów, parametrów konstruktora i przebudowy. Pomimo innej nazwy, zachowanie jest uderzająco podobne: budujesz drzewo elementów interfejsu użytkownika, każdy węzeł jest niezmienny, a gdy stan ulega zmianie, struktura ponownie przegląda to drzewo, aby wygenerować zaktualizowany interfejs.
Jedną z kluczowych różnic jest to, że Flutter jest z założenia wieloplatformowy. Ta sama baza kodu Dart może być przeznaczona dla systemów Android, iOS, web, Windows, macOS i Linux. Compose wychodzi poza Androida (na przykład dzięki Compose Multiplatform), ale historia Fluttera dotycząca obsługi wielu urządzeń jest obecnie znacznie bardziej dojrzała i spójna, co właśnie dlatego wiele zespołów stawiających na Androida bierze to pod uwagę, planując wydanie aplikacji na iOS lub komputery stacjonarne.
Twoja wiedza na temat samej platformy Android jest nadal niezwykle cenna w projektach Flutter. Podczas gdy warstwa interfejsu użytkownika składa się wyłącznie z Darta i widżetów, Flutter opiera się na systemie Android (i iOS) w zakresie uprawnień, konfiguracji systemu, interfejsów API platformy, powiadomień, pracy w tle i wielu innych funkcji, do których dostęp uzyskuje się za pośrednictwem wtyczek i kanałów platformy. Oznacza to, że cała Twoja intuicja dotycząca działania Androida nie idzie na marne – po prostu przesuwa się o jedną warstwę w dół.
Deklaratywny model interfejsu użytkownika: elementy kompozycyjne kontra widżety
Zarówno Jetpack Compose, jak i Flutter implementują deklaratywny model interfejsu użytkownika: opisujesz „jak” powinien wyglądać interfejs użytkownika dla danego stanu, a nie „jak” krok po kroku zmieniać widoki. Zamiast wywoływać settery w widokach, można przebudować drzewo po zmianie stanu i umożliwić wydajne różnicowanie i ponowne rysowanie struktury.
W Jetpack Compose elementy interfejsu użytkownika to funkcje komponowalne, oznaczone adnotacjami @Composable, często skonfigurowany z Modifier. Przycisk może być Button(onClick = ..., modifier = Modifier.padding(16.dp))Łańcuch modyfikatorów ozdabia lub układa obiekt kompozycyjny bez zmiany jego typu bazowego, a Compose używa rekompozycji do odświeżenia tylko tych części drzewa, których dane wejściowe uległy zmianie.
W środowisku Flutter elementami interfejsu użytkownika są widżety — zwykłe obiekty Dart opisujące konfigurację. Są one również niezmienne i uporządkowane w drzewie, ale zamiast przekazywać modyfikator, zazwyczaj przekazuje się argumenty układu lub stylu bezpośrednio za pomocą parametrów konstruktora lub opakowuje się widżet w inne widżety układu. Na przykład można napisać Padding(padding: EdgeInsets.all(16), child: ElevatedButton(...)) aby osiągnąć podobny wynik.
Cykl życia zarówno elementów kompozycyjnych, jak i widżetów jest celowo krótki i niezmienny. Żyją tylko do momentu, gdy nowe dane wejściowe wymuszą ich zastąpienie; żaden z nich nie próbuje zarządzać swoim czasem życia ani mutować bezpośrednio. To zmiana koncepcji w stosunku do starego świata Android View, gdzie widoki były obiektami o długim okresie istnienia, ponownie wykorzystywanymi i mutującymi w czasie. Właśnie dlatego podejście Compose wydaje się tak naturalne we Flutterze.
Pod maską układ obu struktur opiera się na tym samym wzorcu, sterowanym przez nadrzędnego użytkownika i opartym na ograniczeniach. Rodzic dokonuje pomiaru samego siebie, przekazuje ograniczenia, dzieci wybierają rozmiar uwzględniając te ograniczenia, a następnie rodzic pozycjonuje swoje dzieci. We Flutterze zobaczysz to bezpośrednio jako BoxConstraints; w Compose jest to obsługiwane w implementacjach MeasurePolicy. W obu przypadkach rodzice mogą ograniczać dzieci — widżety nie mogą po prostu wybrać dowolnego rozmiaru ani pozycji.
Strukturowanie aplikacji: punkt wejścia, rusztowanie i układy
W systemie Android z funkcją Compose punktem wejścia jest zazwyczaj Activity (często ComponentActivity) gdzie dzwonisz setContent aby hostować Twoje elementy kompozycyjne. Następnie budujesz drzewo kompozycyjne, zwykle zaczynając od MaterialTheme i powierzchnię lub rusztowanie, które definiuje układ wysokiego poziomu.
W Flutterze punktem wejścia jest Dart main funkcja, która wywołuje runApp z głównym widżetem Twojej aplikacji. Ten korzeń jest powszechnie MaterialApp or WidgetsApp widżet, który konfiguruje routing, motywy, lokalizację i nawigator bazowy. Pierwszy „ekran”, który wyświetlasz, często korzysta z Scaffold widget, który pełni bardzo podobną rolę Scaffold w Material 3 Compose: zapewnia pasek aplikacji, treść, pływający przycisk akcji, szuflady itd.
W przypadku prostego tekstu i treści statycznych Compose może domyślnie zawijać treść, dopasowując jej rozmiar do zawartości wewnętrznej, podczas gdy wiele widżetów Flutter domyślnie zajmuje więcej dostępnego miejsca, chyba że istnieją jakieś ograniczenia. Na przykład, jeśli umieścisz Text Można go komponować w kolumnie, ale nie wypełni on automatycznie szerokości. W Flutterze Text wewnątrz a Column może zachowywać się inaczej w zależności od ograniczeń nadrzędnych. Aby wyśrodkować zawartość we Flutterze, często trzeba ją opakować w Center widget lub użyj widgetów układu, takich jak Align, Row, Column, Expanded w połączeniu z właściwościami wyrównywania.
Układy liniowe są mapowane niemal idealnie: Compose ma Row oraz Columni tak samo jest w przypadku Fluttera. W Flutterze przekazujesz dzieci jako List<Widget> i kontrolować odstępy i wyrównanie za pomocą właściwości takich jak MainAxisAlignment oraz CrossAxisAlignmentW Compose polegasz na horizontalArrangement, verticalArrangement, horizontalAlignment oraz verticalAlignmentPrzydatny sposób myślenia: właściwości kończące się na „Arrangement” są odwzorowywane na główną oś Fluttera, a te kończące się na „Alignment” są odwzorowywane na oś poprzeczną.
Gdy potrzebujesz układów względnych lub nakładających się, podejścia są także dostosowane koncepcyjnie. W Android XML możesz sięgnąć po RelativeLayout lub zagnieżdżoną mieszankę LinearLayout oraz FrameLayoutW opcji „Utwórz” możesz utworzyć Row, Column oraz Box (lub napisz własny układ). W Flutterze analogiem jest Row, Column oraz Stack W połączeniu z pozycjonowanymi elementami potomnymi i opcjami wyrównania. Twój model mentalny do układania elementów względem siebie porusza się niemal bez zmian.
Przyciski, wprowadzanie i interakcja
W Jetpack Compose utworzenie przycisku zazwyczaj oznacza użycie Button lub jeden z jego wariantów Materiału, który w Materiale 3 rozwiązuje się do konkretnej implementacji, takiej jak FilledTonalButton. Ty dostarczasz onClick lambda i opcjonalny styl, często za pomocą parametrów takich jak colors lub modyfikatory wypełnienia, szerokości i wyrównania.
W Flutterze odpowiednikiem jest używanie widżetów takich jak FilledButton, ElevatedButton, TextButton or OutlinedButton. Każdy bierze onPressed wywołanie zwrotne i child widget — najczęściej TextMożesz je dostosować, przekazując style przez ButtonStyle lub korzystając z globalnego nadpisania motywu, które umożliwia centralne dostosowanie koloru, kształtu, wysokości i rozmiaru całej rodziny przycisków.
Do obsługi gestów Compose korzysta z modyfikatorów takich jak Modifier.clickable w wielu przypadkach, ale w razie potrzeby można także skorzystać ze specjalistycznych detektorów gestów. Długie naciśnięcia, przeciągnięcia i niestandardowe gesty są zazwyczaj tworzone za pomocą dedykowanych interfejsów API modyfikujących i źródeł interakcji.
Flutter ujawnia jawne GestureDetector widget, który można owinąć wokół dowolnego elementu, który nie ma wbudowanej obsługi gestów. Oferuje szeroki zakres połączeń zwrotnych: onTap, onDoubleTap, onLongPress, onVerticalDragStart, onVerticalDragUpdate, onHorizontalDragEnd i wiele innych. Widżety takie jak ElevatedButton już ujawnić onPressed właściwość, ale w przypadku całkowicie niestandardowych elementów interfejsu użytkownika można użyć GestureDetector lub widżety wyższego poziomu, takie jak InkWell dla sprzężenia zwrotnego materiału.
Wprowadzanie tekstu w Flutterze jest zarządzane za pomocą TextField or TextFormFieldktórego styl jest równoległy ze stylem Compose TextField oraz OutlinedTextField elementy kompozycyjne. Konfigurujesz wskazówki, etykiety, błędy i obramowania za pomocą InputDecoration podobnie jak używasz TextFieldDefaults lub parametry w polach tekstowych Compose. Podobnie jak w Compose, komunikaty o błędach są zazwyczaj wyświetlane reaktywnie, poprzez zmianę stanu i przebudowę dekoracji, zamiast ręcznego manipulowania widokami.
Listy, siatki i przewijana zawartość
Jetpack Compose oferuje dwie główne strategie dla list: prostą Column/Row z iteracją dla małych kolekcji i LazyColumn/LazyRow/LazyVerticalGrid/LazyHorizontalGrid dla dużych lub dynamicznych list. Kontenery typu lazy tworzą tylko to, co jest widoczne, co pozwala na zachowanie wysokiej wydajności przy obsłudze tysięcy elementów.
Flutter stosuje to samo podejście „mały kontra duży”, ale z innymi widżetami. Aby uzyskać małą listę mieszczącą się na ekranie, wystarczy użyć Column i mapuj swoje dane na children. Do wszystkiego, co się przewija, sięgasz ListView or GridView, z konstruktorami, którzy leniwie tworzą elementy potomne tylko wtedy, gdy jest to potrzebne.
Wspólnym wzorcem w Flutterze jest ListView.builder, która odzwierciedla elementy listy leniwej języka DSL biblioteki Compose. Ty dostarczasz itemCount oraz itemBuilder wywołanie zwrotne; Flutter wywołuje ten konstruktor z indeksem od 0 do itemCount - 1 za każdym razem, gdy pojawi się nowy element. W kreatorze możesz zwrócić niemal każdy widżet — od prostego ListTile z tekstem i ikonami do złożonych, niestandardowych wierszy listy.
W przypadku siatek Compose LazyVerticalGrid oraz LazyHorizontalGrid mapa do Fluttera GridView widżet. Zamiast przekazywać liczbę kolumn bezpośrednio do siatki, Flutter często używa delegata, takiego jak SliverGridDelegateWithFixedCrossAxisCount or SliverGridDelegateWithMaxCrossAxisExtent Aby kontrolować układ komórek. Te delegaty obejmują reguły takie jak „liczba kolumn” lub „maksymalna szerokość komórki”, podobnie jak parametry rozmiaru siatki używane w programie Compose.
Zachowanie przewijania jest również analogiczne w obu zestawach narzędzi. Leniwe listy w Compose mają wbudowaną funkcję przewijania; nie trzeba ich owijać w dodatkowe kontenery przewijania. We Flutterze wiele widżetów list i siatek jest przewijalnych, ale w przypadku pojedynczej, niepowtarzalnej zawartości, która powinna się przewijać, można użyć SingleChildScrollView. Tworzenie niestandardowych stron przewijalnych sprowadza się do zagnieżdżania lub łączenia fragmentów w celu bardziej zaawansowanych zastosowań.
Adaptacyjne i responsywne wzorce interfejsu użytkownika
Compose oferuje kilka strategii projektowania responsywnego: niestandardowe układy, BoxWithConstraints, WindowSizeClass i adaptacyjnej biblioteki Material 3. Umożliwiają one zmianę kompozycji w zależności od dostępnej przestrzeni, postawy i kategorii urządzenia. Można je także mieszać w zależności od stopnia skomplikowania projektu.
Flutter nie próbuje bezpośrednio odzwierciedlać tych interfejsów API, ale podstawowa idea jest taka sama: sprawdź ograniczenia i cechy ekranu, a następnie rozgałęzij układ. Dwa główne narzędzia to LayoutBuilder oraz MediaQuery. LayoutBuilder przebiegi BoxConstraints w dół, aby móc zamieniać lub zmieniać kolejność widżetów o określonej szerokości lub wysokości. MediaQuery ujawnia rozmiar ekranu, orientację, wypełnienie i gęstość pikseli dla punktów przerwania wysokiego poziomu.
Zamiast dążyć do bezpośredniego mapowania adaptacyjnych rozwiązań Compose i Flutter, skuteczniejsze jest myślenie w kategoriach wymagań projektowych. Gdy już wiesz, jak Twój interfejs użytkownika powinien dostosowywać się do telefonów, tabletów i komputerów stacjonarnych, możesz wyrazić tę logikę za pomocą Compose WindowSizeClass i adaptacyjne układy lub rozgałęzienia oparte na ograniczeniach i mediach we Flutterze. To samo myślenie projektowe — różne API.
Zarządzanie stanem: Remember kontra StatefulWidget i nie tylko
Jetpack Compose przechowuje ulotny stan interfejsu użytkownika za pomocą remember i posiadaczy państwowych, takich jak mutableStateOf, często w połączeniu z ViewModel i elementy architektury dla dłuższego czasu życia. Gdy stan ulega zmianie, następuje rekompozycja i odpowiednie elementy kompozycyjne otrzymują nowe wartości.
Historia stanu niskiego poziomu Fluttera koncentruje się wokół StatefulWidget i związane z nim State obiekt. Definiujesz widżet, który ma utrzymywać stan, rozszerzając go StatefulWidget, a następnie wdrożyć osobne State<MyWidget> Klasa do przechowywania pól zmiennych. Za każdym razem, gdy aktualizujesz te pola, wywołujesz setState(), co oznacza tę część drzewa widżetów jako brudną i uruchamia przebudowę. Na tym poziomie jest to bardzo podobne do przechowywania stanu kompozycji za pomocą remember i unieważnianie elementów kompozycyjnych, gdy wartości ulegają zmianie.
W przypadku bardziej złożonych aplikacji Flutter opiera się w dużym stopniu na wzorcach społecznościowych i własnych: Provider, Riverpod, Bloc, sklepy w stylu Redux i wiele innych. Działają one jak odpowiedniki stosów architektury Androida: ViewModel + LiveData/Flow + repozytoria w projektach Compose. Centralizują logikę biznesową i udostępniają reaktywne strumienie danych, które napędzają przebudowę widżetów. Znając Compose, wiele z tych wzorców będzie Ci znanych, mimo że interfejsy API się różnią.
Jedną rzeczą, która często zaskakuje programistów Androida, jest to, że zarówno widżety bezstanowe, jak i stanowe we Flutterze często się przebudowują — potencjalnie w każdej klatce animacji. Różnica nie dotyczy częstotliwości przebudowy, lecz miejsca przechowywania stanu zmiennego: StatefulWidget daje ci towarzysza State obiekt, który przetrwa odbudowę, podobnie jak remember pozwala wartościom przetrwać rekompozycję w Compose.
Rysunek, animacja i polerowanie wizualne
Jeśli kiedykolwiek pracowałeś bezpośrednio z systemem Android Canvas oraz Drawable, Komponuj Canvas komponowalność prawdopodobnie wydawała się prosta. Umożliwia deklaratywne rysowanie kształtów, obrazów i tekstu w języku Kotlin, eliminując wiele formalności charakterystycznych dla tradycyjnych widoków niestandardowych.
Flutter udostępnia podobną powierzchnię do rysowania poprzez Canvas API, do którego można uzyskać dostęp przez CustomPaint oraz CustomPainter. Wdrażasz CustomPainter klasa, w której nadpisujesz paint metoda rysowania na płótnie za pomocą Paint obiekty, ścieżki, transformacje itd. Następnie dołączasz tego malarza do CustomPaint widget. Zarówno Compose, jak i Flutter bazują na silniku Skia, więc elementy podstawowe – linie, ścieżki i shadery – wyglądają bardzo znajomo w dwuwymiarowym renderowaniu Androida.
W przypadku animacji Flutter opiera się na wyraźnym systemie animacji zbudowanym wokół AnimationController, Animation<T> i Tweens, a także bogaty zestaw animowanych widżetów. Tworzysz instancję kontrolera (zwykle za pomocą SingleTickerProviderStateMixin dla vsync), zdefiniuj CurvedAnimations lub Tweens, które mapują postęp 0-1 na wartości domeny, a następnie połącz je z widżetami takimi jak FadeTransition, ScaleTransition, AnimatedBuilder lub niejawne widżety, takie jak AnimatedContainerSystem animacji również eksponuje AnimationStatus wywołania zwrotne reagujące na start, zakończenie lub cofnięcie.
Interfejsy API animacji Jetpack Compose są deklaratywne od góry do dołu, z funkcjami takimi jak animate*AsState, przejścia i animowana widoczność. Zamiast ręcznie zarządzać kontrolerami w większości przypadków, opisujesz stany docelowe, a framework steruje interpolacją w czasie. Gdy potrzebujesz bardziej spersonalizowanej kontroli, nadal masz dostęp do prymitywów niskiego poziomu, ale standardowa ścieżka jest bardziej zwięzła niż klasyczny kod XML Androida lub imperatywny kod animacji.
Koncepcyjnie oba zestawy narzędzi wykorzystuje się w ten sam sposób: widżety/elementy kompozytowe zachowują lekkość i przejrzystość, przepuszczają przez nie wartości zmieniające się w czasie i pozwalają frameworkowi obsługiwać interpolację i unieważnianie. Jako programista Compose, dodatkowa przejrzystość Fluttera AnimationController na początku może wydawać się nieco staromodny, ale daje ci bardzo precyzyjną kontrolę nad czasem, krzywymi i orkiestracją.
Stylizacja, motywy, czcionki i zasoby
Nowoczesne aplikacje zależą od dopracowania, dlatego zarówno Flutter, jak i Compose kładą duży nacisk na tematykę i styl. Zastosowania kompozycji MaterialTheme ze schematami kolorów, typografią i definicjami kształtów, a także możesz zagnieżdżać motywy, aby nadpisywać wartości dla poddrzew — w tym wymuszać jasne lub ciemne powierzchnie dla określonych regionów.
W Flutterze odpowiednikiem jest ThemeData przekazany do MaterialApp or Theme widżety. Definiujesz kolory podstawowe, jasność, typografię i motywy specyficzne dla komponentów, takie jak elevatedButtonTheme, textButtonTheme, appBarTheme i więcej. Możesz zastąpić motywy lokalnie, opakowując poddrzewa w Theme widżety, które kopiują element nadrzędny i modyfikują określone pola. Tryb jasny i ciemny można przełączać na poziomie aplikacji, udostępniając theme oraz darkTheme i kontroli themeMode.
Stylizacja tekstu to znane zagadnienie: w programie Compose przekazujesz proste właściwości bezpośrednio do Text lub dostarczyć TextStyle obiekt. Flutter odzwierciedla to za pomocą Text widget akceptujący TextStyle poprzez jego style parametr. TextStyle Obejmuje rodzinę czcionek, rozmiar, grubość, odstępy między literami, wysokość wiersza, dekorację i wiele więcej. Możesz zdefiniować globalne motywy tekstu w ThemeData.textTheme i odwoływać się do nich wszędzie, tak jak korzystałbyś z typografii MaterialTheme w sekcji Utwórz.
Czcionki i obrazy są obsługiwane za pomocą zasobów, a nie tradycyjnego sposobu w systemie Android /res drzewo katalogów. Flutter nie wymusza określonego układu folderów; zasoby deklaruje się w pubspec.yaml a następnie odwoływać się do nich z poziomu kodu. Obrazy są zazwyczaj ładowane za pomocą Image.asset(), który rozwiązuje się do prawidłowego wiadra gęstości na podstawie devicePixelRatio. Logiczne piksele odgrywają tę samą rolę co dp na Androidzie, abstrahując od fizycznej gęstości pikseli.
W przypadku czcionek niestandardowych Compose umożliwia pakowanie zasobów czcionek lub pobieranie ich w czasie wykonywania za pośrednictwem dostawców, takich jak Google Fonts, a następnie łączenie ich z FontFamily i typografii. Flutter korzysta z niemal tego samego schematu: umieszcza pliki czcionek w folderze zasobów, pubspec.yaml, a następnie odwołaj się do rodziny czcionek według nazwy w TextStyleJeśli chcesz czcionek pobieranych w czasie wykonywania, istnieje popularny google_fonts wtyczka udostępniająca funkcje Dart nazwane na cześć czcionek, np. GoogleFonts.robotoTextTheme()—aby szybko wpleść je w swój motyw.
Oba ekosystemy traktują ciągi znaków i lokalizację jako kwestie najwyższej wagi, choć Flutter nie ma bezpośredniego odpowiednika zasobów ciągów znaków XML w systemie Android. Zamiast tego najlepszą praktyką jest przechowywanie tłumaczeń w .arb Pliki i połącz je z łańcuchem narzędzi lokalizacyjnych Flutter. Dostęp odbywa się następnie poprzez wygenerowane klasy Dart, mniej więcej analogicznie do korzystania z R.string identyfikatory w kodzie Androida.
Koncepcje platformy Android przez pryzmat Flutter
Oprócz interfejsu użytkownika jednym z najważniejszych pytań, jakie zadają sobie programiści Compose, jest to, w jaki sposób ich wiedza na temat Androida odnosi się do architektury Fluttera. Na szczęście wiele kluczowych idei — działania, cykl życia, intencje, praca w tle, zasoby, praca w sieci — ma swoje wyraźne odpowiedniki, nawet jeśli interfejs API wygląda inaczej.
W systemie Android, Activity oraz Fragment są Twoimi głównymi ekranami i kontenerami; w Flutterze wszystko jest widżetem, a nawigacja odbywa się za pośrednictwem Navigator oraz Route obiekty. Trasa w przybliżeniu odpowiada jednej aktywności lub fragmentowi, ale zwykle istnieje tylko jeden host Activity na Androidzie z wbudowanym silnikiem Flutter. Trasy są umieszczane i usuwane na stosie Navigatora za pośrednictwem nazwanych tras zdefiniowanych w MaterialApp lub poprzez bezpośrednio skonstruowane PageRoute przypadki takie jak MaterialPageRoute.
Wywołania zwrotne cyklu życia Androida (onCreate, onStart, onResume, itd.) nie mają indywidualnych haków w kodzie Flutter, ale można obserwować cykl życia aplikacji za pomocą WidgetsBindingObserver. Ujawnia stany takie jak resumed, inactive, paused oraz detached, które odpowiadają mniej więcej fazom widoczności, tła i zniszczenia Androida. Kiedy naprawdę potrzebujesz niskopoziomowych haków cyklu życia do zarządzania zasobami, zazwyczaj implementujesz je po stronie natywnej Androida w FlutterActivity lub wtyczki, nie w Dart.
Intencje pełnią w systemie Android dwie role: nawigację w aplikacji i komunikację między aplikacjami. Jak wspomniano, Flutter nie posiada API nawigacji opartej na intencjach – Navigator w pełni zastępuje je w świecie Dart. Do zadań międzyaplikacyjnych (uruchamianie kamery, selektora plików, obsługa intencji udostępniania) zazwyczaj używa się wtyczek, które opakowują niezbędne wywołania Androida (i iOS). Jeśli wtyczka nie istnieje, można napisać własną, używając MethodChannels, aby komunikować się między Dart a kodem natywnym, przekazując intencje i wyniki w postaci komunikatów.
Twoje zrozumienie pracy w tle i wątków również zostaje przeniesione, ale prymitywy wyglądają inaczej. Android wymusza przeniesienie wejścia/wyjścia sieciowego i dyskowego z wątku głównego za pomocą współprogramów, AsyncTask (starsza wersja), WorkManager, JobScheduler, RxJava i tak dalej. Dart z kolei używa jednowątkowej pętli zdarzeń dla każdego izolatu, z async/await dla wejścia/wyjścia i oddzielnymi izolatami dla zadań intensywnie wykorzystujących procesor. W przypadku zadań wymagających wejścia/wyjścia wystarczy oznaczyć funkcje. async, await operację i pozwól pętli zdarzeń zapewnić responsywność interfejsu użytkownika; w przypadku intensywnych zadań procesora należy utworzyć izolat i komunikować się za pomocą przesyłania komunikatów zamiast korzystania z pamięci współdzielonej.
Na froncie sieciowym popularny jest Flutter http Pakiet pełni rolę podobną do OkHttp + Retrofit w podstawowych przypadkach użycia. Ukrywa znaczną część niskopoziomowej pracy gniazd i naturalnie integruje się z async/await. W przypadku złożonych potrzeb można skorzystać z pakietów takich jak dio, ale podstawowy wzorzec pozostaje niezmienny: wykonaj asynchroniczne wywołanie, poczekaj na wynik, zaktualizuj stan za pomocą setState() lub wybranego przez Ciebie menedżera stanu i odbuduj odpowiednie widżety.
Wtyczki, magazyn, Firebase i narzędzia
W systemie Android zwykle deklaruje się zależności w Gradle, natomiast w Flutterze deklaruje się je w pubspec.yaml i pobierz je z pub.dev. Pliki Gradle w ramach android/ Folder projektu Flutter jest przeznaczony głównie do integracji specyficznych dla danej platformy lub gdy potrzebujesz niestandardowych bibliotek natywnych — codzienne tworzenie aplikacji opiera się na środowisku Dart.
Współdzielone preferencje i SQLite również mają gotowe odpowiedniki. Gdzie Android oferuje SharedPreferences w przypadku małych pamięci masowych o wartościach kluczowych i SQLite (lub Room) do danych strukturalnych, Flutter opakowuje je za pomocą wtyczek, takich jak shared_preferences oraz sqfliteTe wtyczki ujednolicają działanie systemów Android i iOS, dzięki czemu możesz korzystać z jednego interfejsu API Dart niezależnie od platformy, korzystając jednocześnie z podstawowych implementacji natywnych.
Integracja z Firebase jest równie prosta i pierwszorzędna. Większość usług Firebase – uwierzytelnianie, Firestore, baza danych w czasie rzeczywistym, komunikatory w chmurze, analityka, zdalna konfiguracja i inne – posiada oficjalne wtyczki Flutter, obsługiwane przez zespoły Firebase i Flutter. Odzwierciedlają one model koncepcyjny z zestawów SDK Firebase dla systemu Android, ale z interfejsami API w stylu Dart. W przypadku bardziej niszowych funkcji Firebase, które nie są bezpośrednio omówione, na stronie pub.dev znajduje się rozbudowany ekosystem wtyczek firm trzecich.
Do debugowania i profilowania pakiet DevTools Fluttera udostępnia bogaty zestaw narzędzi porównywalny bezpośrednio z profilerem i inspektorem układu Android Studio. Możesz przeglądać drzewo widżetów, śledzić przebudowy, monitorować alokacje pamięci, diagnozować wycieki i fragmentację oraz krok po kroku przeglądać kod Dart. W połączeniu z obsługą IDE w Android Studio i VS Code, funkcją gorącego przeładowania i gorącego restartu, cykl sprzężenia zwrotnego w programowaniu we Flutterze wydaje się co najmniej tak samo ścisły – a często nawet skrócony – jak w Compose.
Powiadomienia push, kolejny powszechny problem Androida, są obsługiwane w Flutter za pomocą wtyczek, takich jak firebase_messaging. W tle komunikują się one z Firebase Cloud Messaging i natywnymi frameworkami powiadomień na Androidzie i iOS, ale logika aplikacji znajduje się w ujednoliconym interfejsie API Dart. Konfiguracja i zachowania specyficzne dla platformy (takie jak kanały powiadomień na Androidzie) są nadal istotne, a Twoje dotychczasowe doświadczenia z tymi szczegółami platformy są nadal bardzo istotne.
Nawet widżety ekranu głównego w systemie Android, których nie da się zaimplementować w samym Flutterze, nadal można zintegrować z kodem Flutter. Zazwyczaj tworzy się je za pomocą Jetpack Glance lub układów XML, a następnie używa się pakietu takiego jak home_widget do komunikacji z aplikacją Flutter, udostępniania danych, a nawet osadzania rastrowego interfejsu użytkownika Flutter jako obrazu w natywnym widżecie. To hybrydowe podejście pozwala zachować główny interfejs użytkownika we Flutterze, jednocześnie respektując ograniczenia platformy.
Biorąc pod uwagę wszystkie te paralele, programista Jetpack Compose rozpoczynający przygodę z Flutterem wcale nie zaczyna od zera. Twoje rozumienie deklaratywnego interfejsu użytkownika, cyklu życia Androida, nawigacji, stanu, zasobów i pracy asynchronicznej bardzo naturalnie wpisuje się w świat Fluttera; najbardziej zmieniają się nazwy, język (Dart) i wieloplatformowość. Gdy tylko przyswoisz sobie widżety i Nawigatora jako podstawowe koncepcje, reszta stosu szybko się ułoży.