Am 30. April 2015 wurde auf dem Dart Developer Summit unter dem Codenamen “Sky” ein neues App-Framework vorgestellt, damals als reines Experiment zur Entwicklung von Smartphone-Anwendungen mit der zu diesem Zeitpunkt noch jungen Programmiersprache “Dart”. Kurze Zeit später bekam das Projekt den Namen, unter dem es heute von mehreren Millionen Nutzern weltweit⁰ genutzt wird: “Flutter”.
Die Priorität lag vor allem bei zwei Faktoren: Performance (flüssige Animationen mit konsistent mindestens 60Hz) und Plattformagnostizismus. Darum funktionierten Flutter Apps auch bereits seit dem ersten Beta-Release im Februar 2018 auf sowohl Android- als auch iOS-Geräten¹, seit Flutter 1.0 nicht mal ein Jahr später waren außerdem Implementierungen für MacOS, Windows und Linux sowie eine Webruntime unter dem Namen “Hummingbird” im Gespräch.²
Das Ziel war klar: Flutter-Anwendungen sollten, einmal geschrieben, schnell, schön und vor allem überall laufen.
Geschichte
Zwar lief “Sky” bei den ersten Präsentationen noch ausschließlich auf Android-Geräten³, jedoch war schon damals die Rede davon, das Framework auf Android, iOS “and more”⁴ zum Laufen zu bringen. Als Vorbild nahm man sich dabei die HTML-Rendering-Engine “Blink”, die bereits seit 2013 in allen Chromium-basierten Browsern zum Einsatz kommt und seitdem auf “every platform unter the sun”⁴ portiert wurde. In diese Fußstapfen sollte Flutter als universeller Dart-Renderer treten.
Dass auch die Flutter Engine hierzu das Potenzial hatte zeigte sich unter anderen in der Tatsache, dass bereits vor dem finalen Release Flutters inoffizielle Experimente wie “Flutter Desktop Embedding”⁵ erfolgreich die Flutter-Engine auf MacOS portiert hatten. Mit dem Release Flutters im Dezember 2018 folgte außerdem das Experiment “Hummingbird”, aus welchem später die Flutter Web Engine entstehen sollte. Und bereits im Mai, pünktlich zur Google I/O 2019, wurden beide dieser Experimente Teil der offiziellen Flutter Engine.⁶
Gleichzeitig ging die erste “Technical Preview” von Flutter for Web online, mit welcher jede:r Entwickler:in existierende Flutter-Apps für das Web ausgeben konnte. Als Demo ging außerdem das Spiel “KENKEN” der New York Times online, der ersten veröffentlichten Flutter-Web-App.⁷ KENKEN hatte zuvor auf Flash basiert.
Technischer Hintergrund
Bei der Entwicklung von Flutter for the Web kam es sehr gelegen, dass Dart-Code bereits seit jeher zu Javascript-Code kompiliert werden kann. Somit konnte die “Business Logic” einer Flutter-Anwendung schon mal ohne weitere Aufwände auch im Browser ausgeführt werden.
Flutter auf dem Mobilgerät besteht essentiell aus zwei Komponenten: dem Framework, welches Code zu Widgets zu Bildschirmen verarbeitet, und der Engine (“Skia”), welche diese Bildschirme rendert und auf den Bildschirm zeichnet. Alle Logik findet im Framework statt, die Engine selbst “versteht nichts von Widgets, Physik, Animationen oder Layout”.⁸ Während das Framework allerdings in Dart geschrieben ist (und somit bereits im Browser laufen konnte), ist die Engine in C++ implementiert. Skia konnte somit nicht als Renderer für Webanwendungen verwendet werden.
Eine neue Engine musste her — eine, die nativ im Browser lief und die selbe Erfahrung und Performance liefern konnte wie die bisherige Flutter Mobile Engine. Nach ersten Experimenten, in welchen nur einzelne “Widgets” in DOM-Objekte konvertiert wurden konnten, entstand schließlich aus “Hummingbird” die JavaScript-basierte “Flutter Web-Engine”.
Die Flutter Web Engine hakt sich dort ein, wo sonst Skia oder Funktionen des Mobilgeräts angesprochen werden, und macht an diesen Stellen DOM-Objekte, Canvas-Zeichnungen, CSS-Regeln und PWA-Funktionen aus ihnen. So können ganze Flutter-Anwendungen als Web-Apps nativ im Browser ausgeführt werden, mit der vollen Performance der Browsereigenen HTML (zum Beispiel Blink) und JavaScript (zum Beispiel “V8”) Engines.
Jüngere Versuche ergänzen Flutter Web neben diesem HTML-Renderer außerdem um einen weiteren — “CanvasKit”. Hinter diesem Namen versteckt sich nicht weniger als die komplette, aus Flutter Mobile bereits bekannte, Skia-Engine, neu implementiert in WebAssembly. Diese nutzt nun WebGL, um Flutter Apps aus dem Framework direkt in den Browser zu zeichnen. Der große Vorteil hiervon liegt nahe: Flutter Apps sehen durch CanvasKit genau so aus wie auf dem Mobilgerät oder Desktop und haben auch zwischen Browsern geringere Unterschiede, da immer der selbe Renderer zum Einsatz kommt. Gleichzeitig hat CanvasKit zudem auch die schnellere Performance verglichen mit dem HTML Renderer.¹⁰
Nur zwei Nachteile hat CanvasKit noch: der Skia-Renderer ist etwas größer, was den Download einer Flutter-Webapp beim Laden um etwa 2MB vergrößert.¹⁰ Darum wählt Flutter, wenn nicht explizit anders angegeben, auf Desktopbrowsern CanvasKit und in mobilen Browsern die HTML-Engine.
Darüber hinaus ist, aufgrund der WebAssembly- und WebGL-basierten Engine,die Kompatibilität dieser mit älteren (und mobilen) Browsern noch geringfügig schlechter.
Positionierung
Spricht man über die Entwicklung von modernen, interaktiven Webanwendungen, fallen aktuell hauptsächlich Namen wie “React”, “Vue” oder “Angular”. Für die meisten Entwickler:innen sind Frameworks wie diese das Mittel der Wahl zur Umsetzung von robusten, dynamischen aber sehr flexiblen Webanwendungen¹¹. Für Webseiten jeder Art und Größe konnten diese sich auch bereits beweisen und etablieren — abseits des Internets jedoch nicht. Denn React, Vue oder Angular-Anwendungen sind reine Webapps, die nirgendwo anders als im Browser nativ laufen können.
Zwar ist es möglich, diese Anwendungen auch zu Desktop- oder Mobilanwendungen zu machen. Hierzu werden jedoch zusätzliche Frameworks benötigt, welche eine komplette Browserumgebung emulieren. Am Desktop hat sich hierfür das Framework “Electron” etabliert, welches im Kern ein Chromium-Browser mit etwas tieferem Systemzugriff ist. Smartphone-Äquivalente wie Apache Cordova, Adobe PhoneGap oder Ionic tun etwas ähnliches unter iOS und Android, werden dort allerdings durch den zunehmenden tieferen Systemzugriff von Progressive WebApps zunehmend unwichtiger.
Wollen Entwickler:innen tieferen Zugriff auf die Rendering-Pipeline, um komplett frei 2D- und 3D Inhalte und Oberflächen in den Browser zu rendern, helfen ihnen React etc. auch nicht sehr viel. Hierfür müssen sie in den reinen HTML- und JavaScript-Code, um Zugiff zu APIs wie Canvas oder WebGL zu bekommen. Alternativ werden zusätzliche Frameworks um diese APIs herum benötigt, die solche tieferen Rendering-Funktionen nutzen und Anwendungsfreundlicher machen. Das können simple Wrapper wie three.js sein, oder komplette Frameworks wie Unity, die komplett Plattformagnostisch sind und in WebGL ausgegeben und gerendert werden können.
Flutter ähnelt hierbei Unity sehr, da es ebenfalls das Rendering von reicheren 2D- und 3D-Inhalten ermöglicht, indem es für den Browser die Ausgabe nach Canvas und WebGL anbietet. So kann man in Flutter auch aufwändigere Renderings in den Browser zeichnen, ohne dabei speziell Code für das Web schreiben zu müssen. Mit CanvasKit wäre dies sogar noch trivialer — dann würde Flutter for Web mit der WebAssembly-Implementation von Skia sogar eine komplett eigene Rendering-Engine mitbringen, die uneingeschränkt und unabhänig existierender APIs Inhalte jeder Art anzeigen kann.
Vergleich
Um einzuschätzen, wie tauglich Flutter als Web-Framework ist, wurde das selbe, sehr minimale Projekt in Flutter sowie mehreren anderen Frameworks umgesetzt. Diese Frameworks waren React (als größtes Webapp-Framework und direkte Konkurrenz zu Flutter im Web) und Unity (als einziges ähnlich umfangreiches Cross-Plattform Framework mit Web-Support).
Zusätzlich wurde die Seite, als Kontrollgruppe, in purem HTML umgesetzt.
Diese Testseite bestand aus drei Objekten:
- Dem Text “Hello World” im Standardstil
- Einem willkürlich gewählten 1000x1000 jpg-Bild, skaliert auf 400x400, geladen aus dem Amazon-CDN
- Einem Schalter, der zwischen zwei Zuständen wechseln kann
In allen drei Frameworks wurde das Standardprojekt (z.B. durch “create-react-app”), mit welchem typische Entwickler:innen anfangen würden, abgewandelt.
Hier die vier Ergebnisse im direkten, visuellen Vergleich:
Hierzu ist zu erwähnen, dass es auch unter Unity Third-Party-Workarounds gibt, das gesamte Browserfenster responsive und rahmenlos zu füllen. Alle anderen Frameworks tun dies allerdings selbstständig¹². Ebenfalls ist Unity nicht darauf ausgelegt, von Haus aus Assets wie das Bild aus dem Netzwerk zu laden. Somit ist der fertige Unity-Build der einzige der vier, der die (48,8 KB große) Bilddatei enthält.
Visuell gibt es keine nennenswerten Unterschiede zwischen den vier, die nicht durch ein paar Zeilen CSS gelöst werden könnten. Nennenswert ist allerdings, dass Unity und natives HTML das link/rechts “Switch” Bedienelement nicht unterstützen. React unterstützt es von Werk aus zwar auch nicht, kann allerdings mit drei Zeilen um die Komponente ergänzt werden. Nur Flutter beinhaltet von sich aus einen großen Katalog solcher Komponenten im Material- und Apple-Design.
Wie unterscheiden sich also die Builds in Größe auf dem Server?
Es zeigt sich, dass Flutter Web deutlich größere Builddateien ausgibt als Frameworks wie React, jedoch kompakter ist als WebGL-Exporte aus Unity. Angesichts der Tatsache, dass das Flutter-Projekt ebenfalls eine WebGL Version beinhaltet, ist das nicht schlecht, aber definitiv verbesserungswürdig. Darüber hinaus gilt natürlich zu bedenken, dass sich Flutter Web noch im Beta-Stadium befindet. Mobile Flutter-Apps sind mit jedem Major Release kleiner geworden — man kann also damit rechnen, dass auch bei Flutter Web noch einige Optimierungen anstehen bevor es die Beta verlässt. Für den Moment ist allerdings ein etabliertes Web-Framework wie React, auf reiner DOM-Basis, die kompaktere Lösung.
Aber wie viel dieser Builds werden tatsächlich an den Client übertragen?
Es ist beeindruckend, wie kompakt eine Flutter-App beim Laden ist. Dies ist allerdings in großen Teilen dem “Flutter Service Worker” geschuldet, der beim ersten Laden irgendeiner Flutter-Webapp mit installiert und von da an wiederverwendet wird. Dieser bietet zwar nach offizieller Dokumentation nur PWA-Funktionen wie Offline-Modi, es ist allerdings auch davon auszugehen, dass er (selbst bei geleertem Browser-Cache) das Laden von Flutter-Anwendungen verschnellert. Die “Ressourcengröße” der Flutter-App gibt der Chrome-Netzwerkmonitor mit 2.5MB an, der größte Teil davon “Served by ServiceWorker”. Dokumentiert ist diese Funktion allerdings nirgendwo.
In der totalen Ladezeit ist Flutter tatsächlich die langsamste Option. Hierbei ist allerdings wichtig anzumerken, dass bei Unity etwas geflunkert wird: Wo Chrome die Website zwar bereits komplett geladen hat, wird im Unity-WebGL-Player erst noch ein Ladebalken und ein Spashscreen gezeigt. Dieser Splashscreen lässt sich zwar durch eine Pro-Lizenz entfernen¹³, die Ladedauer bleibt allerdings, wenn auch nicht durch den Netzwerkmonitor erkennbar. Wird diese mit aufgerechnet, ist Flutter wieder “nur” die zweitlangsamste Option.
Fazit
Es ist sicherlich nicht ohne Grund, dass sich Flutter for Web noch im Beta-Stadium befindet; im Vergleich mit deutlich ausgereifteren Web-Frameworks wie React kann Flutter performancetechnisch einfach noch nicht mithalten. Zwar setzt es zur Leistungsoptimierung schon jetzt einige sehr fortgeschrittene und moderne Techniken ein (ein Vorteil dessen, dass Flutter deutlich jünger als andere Frameworks ist), und zeigt großes Potenzial, diese Leistungs-Lücke in den kommenden Monaten und Jahren weiter zu schließen. Es bringt aber im Moment noch zu viel Overhead für sowohl Server als auch Client mit sich, um die Geschwindigkeiten vergleichbarer Web-Frameworks bieten zu können.
Es ist aber auch wahr, dass Flutter als plattformagnostisches Framework deutlich mehr Flexibilität bietet als andere, pure Web-Frameworks. Eine Flutter Webanwendung läuft ohne großen Mehraufwand auch nativ auf praktisch jedem anderen Gerät — von Smartphones über Desktops bis hin zu integrierten Systemen¹⁴. Diese enorme Erleichterung für Entwickler:innen bietet kein anderes Web-Framework, und unter den plattformübergreifenden Frameworks die auch im Web funktionieren (wie Unity) kann sich Flutter leistungstechnisch schon heute sehen lassen.
Plant also jemand, eine reine Webanwendung zu bauen, die niemals außerhalb des Webs laufen soll (außer vielleicht als Progressive Web App), würde ich vorerst noch von Flutter abraten. Reifere Frameworks bieten hier einfach noch mehr. Möchte jedoch jemand eine Anwendung für mehrere Plattformen entwickeln, oder wohlmöglich eine Webanwendung für eine schon bestehende Flutter-App bauen, rate ich auf jeden Fall von reinen Web-Frameworks ab. Hier ist Flutter for Web nach meinem Erachten schon heute produktionsreif genug, um in solchen Fällen hervorragende Arbeit zu leisten.
[0] https://medium.com/flutter/flutter-spring-2020-update-f723d898d7af
[1] https://medium.com/flutter/announcing-flutter-beta-1-build-beautiful-native-apps-dc142aea74c0
[2] https://developers.googleblog.com/2018/12/flutter-10-googles-portable-ui-toolkit.html
[4] https://youtu.be/PnIWl33YMwA?t=288
[5] https://github.com/google/flutter-desktop-embedding
[6] https://developers.googleblog.com/2019/05/Flutter-io19.html
[7] https://medium.com/flutter-nyc/under-the-hood-with-flutter-for-web-bc0d5ce1c11e
[8] https://medium.com/flutter/hummingbird-building-flutter-for-the-web-e687c2a023a8
[9] https://medium.com/flutter/bringing-flutter-to-the-web-904de05f0df0
[10] https://flutter.dev/docs/development/tools/web-renderers
[11] https://star-history.t9t.io/#facebook/react&vuejs/vue&angular/angular
[12] https://github.com/greggman/better-unity-webgl-template