Strubbl am :
Interessanter Einblick in deine Erfahrungen. Meine Brille ist noch etwas rosarot. Mal schauen wie sich die entwickelt
Gut ein Jahr ist es her, dass ich angefangen habe mit Flutter zu arbeiten. Der Auftrag: Eine nutzerfreundliche Anwendung für Android und iOS mit einer gemeinsamen Codebasis zu bauen. Ich fand das Framework zur Mobilanwendungsentwicklung zu Beginn ziemlich toll. Wie sieht es jetzt aus?
Ich finde Flutter immer noch gut, aber habe inzwischen auch mehr von den Schwachstellen mitbekommen. Eine Liste der positiven und der negativen Erkenntnisse folgt.
Bei Flutter baut man die Oberfläche, indem die von der Sprache bereitgestellten Widgets in der Build-Funktion eigener Widgets kombiniert werden. Da hat dann eine Row zwei Columns, in denen jeweils drei Buttons sind, schon hat man ein Grid von Buttons mit zwei Spalten und drei Zeilen. Angelehnt am Material-Design gibt es alle wichtigen Widgets vordefiniert, wenn das nicht reicht können aus ihnen neue gebaut oder mit Plugins fertige eingebunden werden.
Das funktioniert einfach sehr gut. Was in anderen Programmiersprachen in den imperativen Aufrufen unweigerlich zum Chaos wird, bleibt mit diesem Prinzip mit nur wenig benötigter Disziplin gut beherrschbar. Die so entstehenden UI-Files sind viel lesbarer und doch auch mächtiger, als wenn es XML-Dateien wären, aber die Zustandsverwaltung und damit die Logik der App soll eindeutig extern definiert werden. Diese propagierte Trennung funktioniert gut, kann für einfache Screens aber trotzdem ignoriert werden, die Logik wird dann mit in die UI gepackt. Aber das macht man selten. Das ist genau die richtige Mischung aus einem guten Konzept und der benötigten Entwicklerfreiheit.
Als ich anfing war GetX bei uns umstritten. Ich hatte mir das und als Alternative Bloc angesehen und dann darauf bestanden, dass wir GetX zumindest auch benutzen. Mittlerweile hat GetX sich voll bewährt.
Mit GetX definiert man im Controller der UI spezielle Variablen. Wenn eine davon sich ändert, ändern sich auch die UI-Stelle an denen die Variable verwendet wird. Zum Beispiel hat man im Controller ein Set, baut in der UI daraus eine Liste, und wenn das Set ein Element mehr bekommt wird auch die UI-Liste länger. Das gelingt GetX auf der einen Seite mit möglichst wenig Deklarationen, aber gleichzeitig ist der Voodoo-Aspekt viel geringer als bei Bloc. Dass es keine unnötigen abstrakten Konzepte wie Cubits hat kommt noch dazu.
GetX hat ein paar Stolperfallen, so erkennt es nicht automatisch wenn sich der Inhalt eines Elements des Sets ändert. Da muss man nachhelfen, aber immerhin geht das. Insgesamt ermöglicht es eine effiziente Anwendungsentwicklung ohne Bullshit.
Mit pub.dev gibt es eine klare und hilfreiche Anlaufstelle für Plugins. Dort gibt es Plugins für alles was wir bisher gebraucht haben. Einen kompletten Kalender, Kryptographie, Loginformular, Benachrichtigungen, das oben erwähnte GetX – noch viel mehr und insgesamt alles wurde abgedeckt, mit oft hochqualitativ wirkenden und konfigurierbaren Plugins.
Diese zentrale Quelle zu haben ist viel besser als die mühsame Library-Suche bei Android und auch besser als bei allen anderen Programmiersprachen bzw Frameworks, die ich kenne.
Dart hat mich nicht enttäuscht. Ich erwartete das anfangs: Zu hübsch und trotz der Typisierung Ruby-artig war diese Sprache, das konnte nicht wahr sein. Aber es ist wahr. Dart ist einfach ganz wunderbar.
Nicht so dynamisch wie Ruby zu sein schadet ihr an manchen Stellen, vor allem bei der fehlenden Serialisierbarkeit und dem immer noch äußerst umständlichen Umwandeln von Objekten nach JSON (und wieder zurück) schlägt das zu. Aber das erwartete überraschende Fehlverhalten, die frusterregenden Implementierungsfehler – all das blieb aus. Dart würde ich sogar außerhalb von Flutter verwenden, zumindest wenn Ruby nicht verfügbar wäre; vielleicht sogar wenn Ruby verfügbar wäre.
Das größte Problem Flutters ist die respektlose Weiterentwicklung des Frameworks, daher fange ich damit an. Es fehlt den Google-Entwicklern jeglicher Respekt vor der Zeit der Frameworknutzer, was sich in unverschämten und unnötigen Änderungen und völliger Missachtung der Abwärtskompatibilität niederschlägt.
Flutter ist in diesem Jahr von 1.x auf 2.x geklettert. Die große – aber nicht die einzige – Änderung war Null–Safety, was theoretisch optional bleibt aber faktisch durch Plugins erzwungen wird. Null-Safety bedeutet, dass Variablen nicht mehr null
werden können, sind sie nicht entsprechend deklariert worden. int? x
darf ich auf null setzen – die ?-Syntax-Deklaration ist neu –, int x
nicht. Entsprechend muss all der Code der mit einem vorher definierten int x
arbeitet angepasst werden, sodass er niemals x ein null zuweisen kann, oder die Deklaration angepasst und dann der Zustand von x an den Schnittstellen zu anderem null-sicheren Code überprüft werden.
Das vermeidet Abstürze, aber es ist ein Riesenwechsel für bestehende Anwendungen. Gut, es gibt ein Migrationstool – aber es funktioniert nicht. Schon bei unserer vergleichsweise simplen App war es überfordert und quittierte nach Tausend Fehlermeldungen den Dienste. Ich habe die Migration komplett per Hand machen müssen.
Es ist auf der einen Seite verständlich, dass ein junges Framework sich noch ändert. Aber alten Code so unbrauchbar zu machen, Migrationstools beiseite zu stellen die schlicht nicht funktionieren, dafür gibt es keine Entschuldigung.
Und bei dieser einen Änderung bleibt es ja nicht. Im gleichen Atemzug wurden beispielsweise auch einfach mal die bisherigen Buttonklassen deprecated. Und die neuen haben eine völlig andere API, sie werden mit viel kompliziertem Code angepasst. Es ist okay ein neues System für Buttons dem Framework hinzufügen, aber die alten abzuschaffen ist eine völlig unnötige Gängelung.
Hier schlägt das System Google durch: Tausende Entwickler arbeiten am Framework, die kleinen frameworknutzenden Entwicklerstudios verbringen dann ihre Zeit damit den dauernden Änderungen hinterherzurennen. Das ist einerseits monopolsichernd und andererseits ein Strukturproblem der Organisation Google, aber die Ursache zu verstehen hilft in der Praxis wenig.
Schon deswegen würde ich Flutter nicht für ein FOSS-Projekt empfehlen, es ungern selbst in meiner Freizeit nutzen, sogar Firmen gegen die Nutzung raten. Wobei: Android und iOS nativ zu bespielen ist auch nicht besser, wenn eine Mobilanwendung wirklich sein muss ist Flutter immer noch besser als das. Aber was für eine vertane Chance Flutter doch ist, nur durch schlechtes Google-Projektmanagement.
Was für die Sprache gilt, gilt für die Plugins nur noch mehr: Da wird in einem durch fröhlich alter Code kaputtgemacht. Es ist zwar alles auf dem Papier SemVer, doch in der Praxis hält das die wenigsten Entwickler davon ab mit Patch- und Funktionsupdates alten Code invalid zu machen. Migrationsguides sind dann unvollständig und versteckt in Github-Issues, eine Kryptolibrary kann den eigenen Ciphertext nicht mehr lesen, ein Router (ein Modul um die Ansicht zu wechseln) wird so vollständig umgebaut dass jeder Aufruf angepasst werden muss, ohne dass die Dokumentation das auch nur erwähnt. Und spätestens durch Null-Safety wurden die Upgrades nicht-optional, man kann sich ihnen nicht entziehen.
Wenn man es vorher wüsste würde man nur Abhängigkeiten auf Module vernünftiger Entwickler aufbauen. Aber das sieht man im Vorhinein eben nicht immer. Und im Flutterland scheinen die besonders selten zu sein. Vielleicht kommt das aus der früheren Javascript-Herkunft.
Meine Firma macht die Flutterentwicklung mit Visual Studio Code und hat mir ein nettes Laptop gestellt, mit einem Ryzen 7 3700U und 16GB Ram. Das reichte nicht. Ich brauchte zuerst ein Ramupgrade, um beim Kompilieren (mit Emulator und Browser offen, klar) nicht immer wieder ein einfrierendes System zu haben. Und trotz dem modernen und starken Prozessor (und natürlich einer SSD) dauert das Bauen der Androidanwendung inzwischen einfach lange. Schon eher Minuten als Sekunden. Und dabei ist die von uns gebaute App nicht riesig.
Es gibt zwar wie bei Android Studio ein Hot Reload, aber das bringt bei Änderungen der Controller wenig, denn die bleiben bei ihrem alten Zustand. Das genügt also nur in den seltensten Fällen, daher wird viel kompiliert und dabei gewartet. Ich müsste im Grunde an einer Threadripper-Workstation arbeiten. Das macht bei meiner Dauer-Heimarbeit ja sogar Sinn, nur war die ungeplant und sollte das für die Mobilanwendungsentwicklung auch einfach nicht nötig sein.
Flutter kann auch Webapps bauen, aber das Ergebnis sind Javascriptmonster die sich an keine Webkonventionen halten. Nutzer können nichtmal Text kopieren, wird das Standard-Textwidget benutzt. Völlig unverständlich, solche Macken blockieren komplett die Adaption des Frameworks im Webbereich.
Genug von den klar negativen Eigenschaften. So wie Flutter gute und schlechte Seiten hat, sind manche Eigenschaften des Frameworks weder klar negativ noch positiv.
So umschifft Flutter die tieferen Limitierungen der mobilen Betriebssysteme einfach genau gar nicht. Arbeiten im Hintergrund beispielsweise geht unter Android manchmal, unter iOS praktisch nicht. Aber es das Framework hat keine Infrastruktur, um das doch zuverlässig zu können, nichtmal unter dem eigenen Betriebssystem Android. Anderes Beispiel: Geplante Benachrichtigungen beschränkt iOS auf 64, Android auf mehrere Hundert, Flutter und seine Plugins lässt den Entwickler in diesen Unterschied schlicht reinrennen. Unter Android kann man aus einem Formular Zurück-Swipen und dann in einem Dialog vor verlorengehenden Daten warnen, unter iOS geht das nicht. 2018 hat ein Entwickler das Problem nicht verstanden, seitdem ist es im Kern ungefixt.
Wäre Flutter nicht von Google könnte man dem Framework solche Limitierungen nur schwer anlasten. Aber weil Google mit seiner Größe und Rolle als Androidentwickler die Möglichkeiten hätte hier Lösungen anzubieten, kann ich das dem Framework nicht einfach durchgehen lassen. Es stellt zwar in manchen Bereichen eine brauchbare gemeinsame Basis für die Entwicklung von iOS- wie Androidanwendungen her, das ist der positive Teil. Aber es übertüncht die Unterschiede ungenügend, aber macht es dabei trotzdem schwerer die Stärken von Android zu nutzen, wie dessen eigentlich funktionierende Hintergrundthreads via WorkManager oder Vorgängerlösungen. Und das macht Flutters Vorzüge etwas kaputt.
In Flutter werden Widgets einmal deklariert, dabei konfiguriert, später reagieren sie nur noch auf Interaktionen vom Nutzer oder Zustandsänderungen. Das ermöglicht den oben gelobten expliziten Widgetbaum, das ist positiv. Aber Widgets so gar nicht imperativ manipulieren zu können ist manchmal hochproblematisch. Wie schließt man zum Beispiel auf Knopfdruck das geöffnete Overlay eines Autocomplete-Widgets? Nur durch einen Hack, indem man mit dem FocusManager dem Widget den Fokus entzieht.
Andere Dinge gehen manchmal einfach gar nicht. Hier müssten die Entwickler eine bessere Mischung zwischen Konzepttreue und Entwicklerbefähigung finden.
Flutter will Anwendungen mit 60 FPS zeichnen, inzwischen sogar mit 120 FPS auf entsprechenden Geräten. Was ist mit 360 FPS bei Webanwendungen? Okay, bleiben wir fair. Aber auch das erklärte 60-FPS-Ziel ist illusorisch, wenn beim ersten Öffnen einer App es an allen Ecken und Enden laggt. Da müssen z.B. noch die Shader kompiliert werden, was gerade unter iOS massiv zu Einschränken führt. Erst die gerade veröffentlichte Version 2.5 schafft da vielleicht Abhilfe.
Es stimmt, dass die gebauten Oberflächen ansonsten meist performant laufen. Deswegen ist der Punkt nicht unter den schlechten Seiten eingeordnet. Aber können das nicht im Grunde alle Frameworks zur mobilen Appentwicklung? Sobald etwas im Hintergrund rennt muss das auch bei Flutter manuell in einen Thread geschoben werden, was nicht immer geht, dazu kommen dann die Shader-Stotter. Vielleicht bewerte ich hier Flutter sogar zu positiv.
Und schließlich ist da noch die Testumgebung. Flutter kann Unit-, Widget- und nun auch Integrationstests. Bei Unittests sollen einzelne Bestandteile des Codes getestet werden, mit Widgettests der gewünschte Aufbau der UI kontrolliert, mit Integrationtests der Ablauf der kompletten App. Das ist alles eingebaut und Tests zu schreiben ist mit dem System keine Qual, immerhin.
Aber andererseits sind die Testumgebungen für Unit- und Widgetttests lächerlich beschränkt. Unittests können im Grunde nichts machen außer lokal Dart-Code ausführen, aber nicht mit dem System interagieren, keine HTTP-Requests senden. Soll eine API-konsumierende Funktion getestet werden darf man die API dann mocken. Es gibt zwar Leute, die das für die richtige Art halten Unit-Tests zu schreiben. Aber ich halte das für eine irrige Position, denn es führt zu einen riesigen Aufwand und verhindert, dass Tests echte Fehlerfälle erkennen.
Bei bei den Integrationtests gibt es diese Beschränkungen zwar nicht, aber dafür gibt es sie erst seit kurzem. Integrationtests funktionieren erst seit einem Umbau des Systems, der erst jetzt fertig wurde. Davor waren sie undokumentierterweise einfach kaputt, ich zumindest kriegte sie partout nicht zum Laufen. Und das ging wohl vielen so. Sie müssen auch immer noch auf simulierten oder echten Geräten laufen, was in einer CI-Infrastruktur aufwendig und damit im Zweifel teuer ist.
Flutter ermöglicht das Testen des Codes. Vielleicht macht es das besser als andere Frameworks in dem Bereich. Aber es ist viel komplizierter als ideal.
Ich mag Flutter immer noch. Man muss sich vor Augen halten, wie hochproblematisch die native Anwendungsentwicklung sein kann, schon wirken die Probleme des Frameworks weniger gewichtig. Dart ist sogar toll. Insgesamt schafft mein kleines Team tolle Arbeit und auch ich fühle mich mit Flutter produktiv. Meine Nutzertests bestätigen den positiven Eindruck des Ergebnis, das war dann sogar großartig.
Aber es ist dann eben doch ein Google-Framework, seine hässlichen Seiten passen genau dazu. Serendipity versucht wahrscheinlich seit fast 20 Jahren mit einem Mini-Team Abwärtskompatibilität beizubehalten, bei Flutter kriegt ein Milliardenunternehmen es nicht mein erstes aktive Jahr hin.
Auch die Stärken passen: Cross-Plattformanwendungen mit wenig Aufwand und einem so guten Konzept bauen zu können passt zu der technischen Leistungsfähigkeit dieses Unternehmens. Die sieht man am Anfang, daher damals mein erster sehr positiver Eindruck. Die Schwächen sieht man dann später, daher mein jetzt etwas durchwachsenerer.
Ich hoffe, meine Bewertung kann dem einen oder anderen Leser bei seiner Entscheidung helfen, ob er Flutter verwenden sollte oder nicht. Ich möchte wirklich nicht zu stark von abraten – die beworbenen Stärken sind ja da. Nur sollte man sich der Schwächen des Frameworks ebenfalls bewusst sein.
Interessanter Einblick in deine Erfahrungen. Meine Brille ist noch etwas rosarot. Mal schauen wie sich die entwickelt
Magst du teilen, was du mit Flutter baust?
artodeto's blog about coding, politics and the world am : Die KW 41/2021 im Link-Rückblick
Vorschau anzeigen