Od wielu lat trwa batalia pomiędzy aplikacjami natywnymi a narzędziami cross-platformowymi. Przez ostatnie lata powstało kilka technologii takich, jak Xamarin, PhoneGap, React Native, jednak żadna z nich nie miała takiego wpływu na całe środowisko mobilne jak technologia stworzona przez Google’a w 2018 roku – Flutter.
Czym jest Flutter i jak działa?
Flutter jest pierwszą technologią w historii, która pozwala na implementację natywnych aplikacji mobilnych w jednym środowisku.
Flutter pozwala za pomocą jednej bazy kodu źródłowego tworzyć aplikacje natywne zarówno na iOS, jak i Androida. Dzięki temu, że rdzeń aplikacji powstaje w jednym systemie, zawierającym własne widgety (czyt. komponenty graficzne), wygląd aplikacji jest taki sam zarówno na urządzeniach z systemem Android, jak i iOS.
Kod aplikacji pisany jest w dość przyjemnym w pisaniu, prostym języku Dart, również rozwijanym przez przedsiębiorstwo Google. Szybko rosnący ekosystem oraz społeczność aktywnych developerów powoduje to, że Flutter przejmuje część rynku aplikacji hybrydowych, a nawet czysto natywnych.
Działanie Fluttera możemy porównać do popularnych silników graficznych typu Unity czy Unreal Engine. Nie korzystamy w tym przypadku z natywnych interfejsów, ponieważ całość jest renderowana w specjalnie przygotowanym kontenerze. Dzięki temu unikamy bezpośredniego połączenia z natywnym API, co eliminuje potencjalne problemy przy aktualizacjach systemów, czy samych komponentów. Oczywiście nie możemy zapomnieć o wspólnym kodzie źródłowym dla obydwu platform, dzięki czemu jeden programista może rozwiązywać problemy zarówno platformy iOS jak i Android.
Popularność Fluttera
Prostota narzędzi programistycznych, jak i samego języka Dart obniża próg wejścia w zawód programisty dla nowych osób. Oczywiście jak każda nowa technologia, stworzona przez giganta pokroju Microsoft, czy tak jak w tym przypadku Google, narażona jest na ryzyko zamknięcia całego projektu, przy nie sprostaniu oczekiwaniom firmy. Niemniej Flutter już mocno zakorzenił się na rynku aplikacji mobilnych a tempo wprowadzania nowości wraz z szybko rosnącym zainteresowaniem społeczności, pozwala wierzyć, że Flutterowi już to nie grozi.
Poniższy wykres z Google Trends, pokazuje jak rośnie popularność Fluttera w stosunku do innych technologii na świecie w okresie ostatnich 5 lat.
Nasze doświadczenie z technologią Flutter
W Appchance mieliśmy przyjemność zrealizować już 4 duże projekty w technologii Flutter:
Jakie mieliśmy obawy ?
Zawsze z dystansem podchodziliśmy do technologii hybrydowych, które ze względu na swoje skomplikowanie narzucały pewne ograniczenia, czy to w postaci mniejszej wydajności, czy samego programowania.
Rozpoczynając pracę z Flutterem mieliśmy obawy dotyczące:
- Połączeń z natywnym API. Obawialiśmy się zarządzania wiadomościami push, lokalizacją użytkownika czy mapami. Dodatkowo podchodziliśmy z pewną dozą niepewności do architektury oraz sposobu pisania aplikacji.
- Wystarczającej bazy gotowych bibliotek open source dla tak świeżej technologii
Czy nasze obawy były słuszne?
Nasze pierwsze doświadczenia w pracy z technologią Flutter dosyć szybko zweryfikowały nasze obawy. Poniżej zebraliśmy główne spostrzeżenia i wnioski:
- Nasza obawa dotycząca połączeń z natywnym API (czyt. notyfikacji czy lokalizacji) nie potwierdziła się. Jest dużo gotowych bibliotek, które pomagają zarządzać uprawnieniami czy pobierać lokalizację użytkownika. Oczywiście nie wszystko można rozwiązać za pomocą bibliotek, ale napisanie własnej również nie stwarza większych problemów. Pozytywnie zaskoczyła nas liczba gotowych już bibliotek oraz ogromna społeczność wokół Fluttera.
- Według nas jest stosunkowo mało źródeł wiedzy zaawansowanej, większość poradników w internecie dotyczy rozwiązań dość błahych problemów. Podczas tworzenia aplikacji produkcyjnej ze skomplikowanym UI byliśmy w zasadzie zdani na własną twórczość. Należy jednak podkreślić, że wraz z rosnącą popularnością Fluttera ilość poradników dotyczących bardziej zaawansowanych rozwiązań, systematycznie się zwiększa.
- Na przestawienie się pod kątem developerskim z mechanizmów natywnych na Flutterowe, należy poświęcić trochę czasu. Musieliśmy zmienić nawyk korzystania ze sprawdzonych rozwiązań natywnych, które nie miały zastosowania przy technologii Flutterowej.
- Według naszej oceny rozwiązanie “Hot Reload” czyli odświeżenie widoku ekranu bez ponownej kompilacji całego projektu, ma duży wpływ na szybkość developmentu, a także samego komfortu pracy programistów. “Hot Reload” daje niesamowicie duże możliwości nauki. Jeśli jakiś komponent nie spełnia naszych oczekiwań, zmieniamy go na inny, zapisujemy, a na ekranie telefonu widzimy już rezultat. W taki sposób można szybko spróbować różnych możliwości czy kombinacji w skomplikowanych widokach aż znajdziemy pożądane rozwiązanie. Dzięki temu szybciej można zrozumieć jak działają komponenty udostępnione we Flutterze.
Nasza rada
Osobie z wyrobionym już doświadczeniem mobilnym (np. programiście iOS), polecamy po odbyciu krótkiego kursu/zapoznaniu się z tą technologią, podjęcie prób napisania własnej aplikacji. Może to być mały projekt, który wcześniej zaprogramowaliśmy natywnie. Zupełnie nowej osobie, pragnącej rozpocząć pracę jako Flutter Developer, polecamy rozpocząć przygodę od kursu np. https://www.appbrewery.co/p/flutter-development-bootcamp-with-dart . Dodatkowo warto zapoznać się dobrymi praktykami programowania. Flutter przez swoją łatwość budowania interfejsu pozwala trochę “nabałaganić “ w kodzie. W celu uniknięcia przyszłych frustracji w stylu „kto to pisał?!”, dobrze byłoby zasięgnąć solidnych podstaw w programowaniu ogólnym.
Case study – nasz projekt oparty na Flutterze
Rozpoczynając pracę nad naszą pierwszą wykonaną we Flutter, produkcyjną aplikacją FitKiDDO, byliśmy pełni optymizmu i nadziei. Nasze próbne aplikacje, na których uczyliśmy się tej technologii przebiegały zwykle bez większych problemów.
- Zacznijmy od architektury, zdecydowaliśmy się na rozwiązanie o nazwie Bloc, wykorzystując bibliotekę Felia Angelova https://bloclibrary.dev/#/. Posiada ona obszerną dokumentację, przedstawiająca użycie w kilku różnych przypadkach (np. aplikacja do temperatury, TODO list itd.). Bloc naszym zdaniem podobny jest do architektury MVVM, którą z powodzeniem stosowaliśmy w natywnych aplikacjach iOS-owych oraz Androidowych. Różnica przy Bloc-u polega na operowaniu na stanach. Wynikiem operacji, które przeprowadza Bloc (swoisty ViewModel) są stany, na które odpowiednio reaguje nasz Widget. Początkowo problematyczne wydawało się przestawienie na operowanie stanami. Kluczowe w tej sytuacji jest odpowiednie nazewnictwo i rozróżnienie, co jest stanem a co nie. Odpowiednie rozpisanie i przygotowanie stanów znacząco upraszcza zarządzanie Widgetem. Polecamy, aby w przypadku bardziej skomplikowanych widoków zatrzymać się na chwilę przed rozpoczęciem implementacji i zastanowić się, jak i na co nasz Widget powinien reagować.
- Widgety – Flutter dostarcza wiele gotowych komponentów, które zwykle pozwalają na znaczną konfigurację i dostosowanie do własnych potrzeb. Zauważyliśmy, że “przykrywaliśmy” gotowe komponenty swoimi klasami, często niepotrzebnie. Dlaczego? Otóż zwykle zaczynaliśmy od dość prostych wariantów danych komponentów, np. ukrywaliśmy przycisk, aby miał zawsze zaokrąglone rogi, odpowiedni rozmiar, czy odpowiednio sformatowany tekst. Dzięki temu ograniczyliśmy jego konfigurowalność do minimum. Okazało się to błędną drogą, ponieważ w miarę postępów w projekcie musieliśmy co chwilę rozszerzać nasze komponenty, ponieważ dochodził np. poziom zaokrąglenia, w innym miejscu font powinien być trochę inny, a w innym trzeba było dodać cień itd. Koniec końców okazywało się, że nasze komponenty przyjmują niewiele mniej parametrów co ich bazowe odpowiedniki. Zmieniliśmy to podejście. Obecnie staramy się na siłę nie “przykrywać” natywnych komponentów tylko korzystać z dekoracji czy stylów, które Flutter oferuje. Mamy klasy, które zarządzają stylami w aplikacji i tak np. jeśli design aplikacji przewiduje zaokrąglony przycisk ze zdefiniowanym cieniem pod akcję, to tworzymy dekorację dla takiego przycisku. Dzięki temu używamy bazowych komponentów, dekorując je do naszych potrzeb.
- Skalowanie UI – Problemem było dostosowanie rozmiaru widoków do ekranu/dostępnego miejsca. Na początku próbowaliśmy obejść ten problem, przeliczając rozmiar ekranu na skalę i w ten sposób dopasowywać rozmiary widgetów czy fontów do ilości dostępnego miejsca. Dosyć szybko zmieniliśmy podejście, ponieważ nie byliśmy w stanie sprostać tysiącom urządzeń, aby na każdym w 100% widok był dobrze zeskalowany. Rozwiązaniem tego problemu był sam Flutter i jego gotowe komponenty. Flutter dostarcza nam komponenty, które same skalują widok do dostępnego rozmiaru. Używając Column czy Row, wystarczy “opakować” widget w Expanded/Flexible I nadać mu odpowiednia skalę w stosunku co całości (flex). W przypadku dużych widoków nie unikniemy “opakowania” całości w ScrollView.
- Grafiki SVG/PDF – Problemem okazał się brak wsparcia assetów (tutaj obrazów) w formatach SVG. Do obsługi SVG jest gotowa biblioteka, która częściowo rozwiązuje ten problem, niestety nie wspiera ona części efektów czy stylów. Wprawdzie opisuje ona, w jaki sposób eksportować grafiki z Adobe Illustratora, aby były wspierane, niestety u nas nie zawsze to działało. Szczególnie dla bardziej złożonych struktur. Rozwiązaniem było zastosowanie standardowych ikonek w formacie PNG lub dla mniej złożonych można zastosować narzędzie FlutterIcon.com, które konwertuje ikony do pliku ttf. Daje to nam możliwość korzystania z ikon w identyczny sposób jak gotowy Flutterowy pakiet z materiałowymi ikonkami (Icons.add etc.).
Podsumowanie
Nasze doświadczenie w pracy z Flutterem na chwilę obecną jest znacznie bogatsze. Każdy kolejny projekt upłynnia naszą pracę z technologią Flutter. W kolejnych artykułach podzielimy się dalszymi spostrzeżeniami i wnioskami płynącymi z wdrożeń Flutterowych, których realizujemy coraz więcej.