RoboCitrus Logo
Ist Flutter schnell genug? Wann du Rust brauchst und wie du es herausfindest (KI-generiertes Titelbild) Titelbild: KI-generiert

Ist Flutter schnell genug? Wann du Rust brauchst und wie du es herausfindest

Veröffentlicht:
1 min Lesezeit

Die falsche Frage

"Ist Flutter schnell genug?" Die Frage höre ich stÀndig. Meistens ist sie falsch gestellt.

Auf der FlutterCon 2026 habe ich zwei Talks gehört, die auf den ersten Blick nichts miteinander zu tun haben. Im einen ging es darum, Rust per FFI in Flutter einzubinden, um Bilder extrem schnell zu verarbeiten. Im anderen darum, warum ein harmlos aussehender Screen die GPU und den Akku leer zieht.

Zusammen ergeben sie fĂŒr mich eine klare Linie: Erst messen, dann entscheiden. Manchmal lautet die Antwort Rust. Öfter lautet sie: Du hast irgendwo einen Ticker laufen, der nie schlafen geht.

Talk 1: Wenn Dart wirklich zu langsam ist

Der Speaker hatte eine automatisierte Pipeline, die Bilder generiert. Raus kamen Full-HD- oder 2K-Bilder, und die mussten auf 4K hochskaliert werden. Mit dem Dart-Paket image dauerte dieser eine Schritt auf einem MacBook M4 rund 10 Sekunden. Bei Tausenden Bildern pro Tag ist das ein Problem.

Er hatte zwei Möglichkeiten. Das Paket selbst verbessern. Oder nativen Code anbinden. Er hat sich fĂŒr Rust entschieden und daraus das Paket Pixer gebaut.

Die Zahlen aus dem Talk:

  • Full HD auf 4K hochskalieren: etwa 6 Sekunden mit image, etwa 70 Millisekunden mit Pixer. Das ist grob Faktor 100.
  • Hochskalieren plus JPEG-Encoding: laut Speaker etwa Faktor 1000.

Woher kommt das? Rust wird mit LLVM zu sehr effizientem Maschinencode kompiliert. Dazu kommen SIMD-Instruktionen, Multithreading beim Resize und gut optimierte Algorithmen aus dem Rust-Ökosystem. Der Speaker meinte auch, dass das image-Paket an einigen Stellen nicht besonders compiler-freundlich geschrieben ist.

Warum Rust und nicht C++ oder Swift/Kotlin?

Seine Argumente fand ich schlĂŒssig:

  • Rust hat mit Cargo einen richtigen Paketmanager und bringt Speichersicherheit mit. Bei C und C++ fehlt beides.
  • Native Swift- und Kotlin-Implementierungen mĂŒsstest du mehrfach schreiben. Mit Rust gibt es eine Codebasis fĂŒr alle Plattformen.
  • Flutter Rust Bridge hat er bewusst nicht genutzt, weil er die Kontrolle behalten wollte. Sein Rat fĂŒr Einsteiger: erst den manuellen Weg lernen (cbindgen erzeugt C-Header aus Rust, ffigen daraus Dart-Bindings), danach kann man zur Bridge wechseln.

Die Stolpersteine bei FFI

Drei Dinge fand ich besonders lehrreich.

Wenige ÜbergĂ€nge. Jeder Aufruf ĂŒber die Grenze zwischen Dart und Rust kostet. Pixer nutzt deshalb ein Builder-Pattern: Du verkettest Operationen wie Resize oder Blur, es passiert aber noch nichts. Erst beim Speichern geht alles gesammelt in einem einzigen Aufruf an Rust. Die C-Schnittstelle hat nur etwa zehn Funktionen.

Speicher. Das Bild liegt auf dem Rust-Heap, Dart hĂ€lt nur einen kleinen Zeiger. Der Garbage Collector sieht also nur das Mini-Objekt und rĂ€umt die 12 bis 100 MB Bilddaten nie auf. Die Lösung: Finalizable plus NativeFinalizer, der beim AufrĂ€umen free auf der Rust-Seite aufruft. Dazu der Parameter externalSize, damit der GC die echte GrĂ¶ĂŸe kennt und entsprechend reagiert.

Distribution. Native Bibliotheken pro Plattform zu bauen und einzubinden ist schmerzhaft. Pixer nutzt Build Hooks und lĂ€dt fertig kompilierte Binaries von GitHub Releases, inklusive Hash-PrĂŒfung. Niemand braucht eine Rust-Toolchain. Die Binaries sind etwa 2 MB (ARM) und 5 MB (x86) groß. Wer nur PNG braucht, kann per Cargo Features nur diese Codecs mitkompilieren.

Wann FFI laut Talk Sinn ergibt

Wenn Dart einen bekannten Flaschenhals hat und es eine optimierte native Lösung gibt. Typisch: Krypto, Bild, Audio, Video, große Datenmengen auf dem GerĂ€t. Nicht sinnvoll: sehr hĂ€ufige, aber triviale Aufrufe, bei denen der GrenzĂŒbergang mehr kostet als er bringt.

Und der wichtigste Satz: Nicht Rust um der Rust-Willen. Erst Dart-Code benchmarken und profilen, nur bei bestÀtigtem Problem nativ gehen.

Talk 2: Wenn nicht Dart langsam ist, sondern die GPU schwitzt

Der zweite Talk drehte die Frage um. Das Beispiel war ein Screen im Stil von ClickUp: ein Dialog mit unscharfem Hintergrund und ein CupertinoTextField. Sieht simpel aus. Ist es nicht.

Was Blur wirklich kostet

Blur braucht Informationen von benachbarten Pixeln. Die GPU rendert aber in Kacheln. Sie muss also erst den Hintergrund fertig rendern, in den Speicher schreiben, wieder einlesen, weichzeichnen und dann die UI obendrauf legen. Ein Frame auf einem aktuellen iPhone hat laut Talk etwa 24 MB, hin und zurĂŒck verdoppelt sich der Speichertraffic. Aus einem Render-Pass werden mehrere.

Einmal berechnet ist das kein Drama. Im Xcode-Energieprofil sah der Screen im Ruhezustand sogar entspannt aus, weil gar keine neuen Frames angefordert wurden.

Der eigentliche TĂ€ter: ein Ticker

Sobald das Textfeld den Fokus bekam, sprang die GPU-Last hoch. Der Grund ist der blinkende, genauer gesagt ausfadende Cursor. Er hÀngt an einem AnimationController, und ein aktiver Ticker fordert Frames in voller Display-Rate an, auf manchen GerÀten also 120 pro Sekunde. Die UI Àndert sich vielleicht achtmal pro Sekunde, aber der Blur wird 120 Mal berechnet.

Ein frĂŒherer Flutter-PR hat die Cursor-Updates auf 8 Hz gesenkt. Das hilft CPU und Rebuilds, aber der Ticker fordert weiter volle Frames an. Folge: Hitze, Akku leer, Throttling, im schlimmsten Fall Frame Drops.

Merksatz aus dem Talk: Ein Demo mit 120 FPS ist nicht automatisch nachhaltig. Schnell und billig sind zwei verschiedene Dinge.

Was du dagegen tun kannst

  • Ticker kontrollieren. Nicht jede Animation braucht volle Framerate. Das Paket FixTicker bietet eine Ticker-Ă€hnliche API, die Frames ĂŒber Timer plant.
  • Cursor-Fade abschalten. Beim CupertinoTextField lĂ€sst sich der Cursor auf Blinken statt Fading umstellen, wenn dir die Optik egal ist.
  • Tickers hinter Sheets stoppen. Flutter schaltet Ticker hinter komplett deckenden Routes stumm, hinter nicht deckenden (etwa Sheets) nicht. Das musst du selbst erledigen.
  • Blur klug einsetzen. Ein Backdrop-Blur lĂ€sst sich fĂŒr mehrere Elemente wiederverwenden. Wenn du Blur animierst, muss die Animation exakt bei 0.0 enden. Ein winziger Restwert kostet trotzdem einen zusĂ€tzlichen Pass. Und die Kosten steigen mit dem Sigma nicht linear, sondern in Stufen, weil höhere Werte mehr Downsampling erlauben.
  • Kein Opacity ĂŒber komplexen BĂ€umen. Das erzwingt Offscreen-Rendering. Besser mit transparenter Farbe malen oder Opacity direkt an Bilder geben.
  • ShaderMask ersetzen. FĂŒr Fades am Listenrand reicht bei bekanntem Hintergrund oft ein Gradient-Overlay.
  • Layer eng halten. Je kleiner die FlĂ€che, desto kleiner die Texturen.

Profiling

Die Tools: DevTools fĂŒr CPU und UI, RepaintBoundary zum Nachvollziehen von Paints, auf iOS Metal System Trace und Metal Frame Capture in Xcode. Dort siehst du die einzelnen Passes: Hintergrund, Downsample, vertikaler Blur, horizontaler Blur, Compositing. Der Workflow im Talk: Profile-Build, GPU Frame Capture im Scheme aktivieren, Frame aufnehmen, Dependencies anschauen.

Die Speaker haben außerdem ein experimentelles Paket gezeigt, das Frame-Kosten in Widget-Tests schĂ€tzt, und drei Skills fĂŒr KI-Agenten: Was macht den Frame teuer, was hĂ€lt das Zeichnen am Laufen, wie profile ich. Ein offener Flutter-PR soll Ticker nur noch Frames anfordern lassen, wenn sich wirklich etwas Ă€ndert. Er wurde laut Talk bereits zweimal gemerged und zweimal reverted.

Mein Fall: Der Sitzplan-Algorithmus in DragonBoat Hub

Bei mir ist Rust kein Theorie-Thema. Ich nutze es in DragonBoat Hub, genauer fĂŒr den Algorithmus, der die Sitzverteilung im Drachenboot berechnet. Er verteilt Paddlerinnen und Paddler nach Gewicht, Erfahrung und Balance auf die Sitze, und zwar mit vielen Versuchen, bis die beste Aufstellung steht.

Die Geschichte des Algorithmus ist ein ziemlich ehrliches Beispiel fĂŒr "erst messen, dann umbauen":

  1. JavaScript: Das Original ist DragonTrim von Christian, in JS geschrieben.
  2. Dart-Port: Im FrĂŒhjahr habe ich den Algorithmus nach Dart portiert, damit er direkt im Serverpod-Server lĂ€uft.
  3. JS als Sidecar: Ende April habe ich den Dart-Port wieder rausgeworfen und das JS-Original als Sidecar-Prozess laufen lassen.
  4. Rust-Sidecar: Heute lĂ€uft ein Rust-Port als Sidecar. Er wird per Differential-Test gegen das JS-Original geprĂŒft und liefert fĂŒr dieselbe Anfrage byte-identische Antworten.

Ich habe das am 17.10.2026 auf einem Apple M4 Pro nachgemessen. Eingabe war ein realistisches 20er-Boot mit 20 Paddlern, Propulsion-Balancing an und 100 Versuchen, so wie in der Produktion:

ImplementierungMedian
Dart-Port (Deep-Variante)1031,6 ms
Dart-Port (Basis-Variante)278,8 ms
JS-Original, Node103,7 ms
JS, Bun61,9 ms
Rust, ein Thread7,7 ms
Rust, alle Kerne1,1 ms

Über HTTP lokal sind es 1,7 ms fĂŒr den Rust-Sidecar gegen 97,8 ms fĂŒr den Node-Sidecar.

Zwei Dinge dazu, damit das Bild nicht schief wird. Erstens: Ich hatte den Dart-Port frĂŒher mit "mehrere Sekunden" im Kopf. Gemessen sind es auf dem Laptop rund eine Sekunde. Auf einem kleinen Server kann es lĂ€nger dauern, belegt habe ich das nicht. Zweitens: Eine Sekunde ist fĂŒr sich genommen nicht dramatisch. Aber 1 Millisekunde statt 100 oder 1000 Millisekunden Ă€ndert, was du damit anstellen kannst, und die Last pro Anfrage auf dem Server schrumpft entsprechend.

Das passt genau zu dem, was der Pixer-Talk gezeigt hat: Rust lohnt sich bei echter Rechenarbeit mit einem klaren Flaschenhals. Und es passt zu meiner Reihenfolge unten. Ich bin nicht gleich zu Rust gesprungen, sondern erst durch Dart und JS gegangen und habe jeweils gemessen. Der eigentliche Punkt fĂŒr mich: Der Differential-Test gegen das JS-Original ist das Sicherheitsnetz, nicht mein Vertrauen in den neuen Code. Solange beide dieselben Antworten liefern, kann ich die Implementierung tauschen.

Rust lĂ€uft bei mir aber nicht nur auf dem Server. Die Bewertung, wie gut ein Boot besetzt ist, steckt in einem eigenen Rust-Crate, und genau dieses Crate binde ich auch in die Flutter-App ein, per flutter_rust_bridge. Der Grund ist nicht Tempo, sondern Konsistenz. FrĂŒher hatte die App eine Dart-Nachbildung der Bewertung, und die beiden Seiten waren auseinandergelaufen: Manche Strafpunkte waren in der App auf null gesetzt, auf dem Server kosteten sie 10000. Die Zahl, die ein Trainer in der App gesehen hat, war nicht die Zahl, die der Optimizer minimiert. Heute rechnen App und Server mit demselben Code, und ein ParitĂ€tstest hĂ€lt beide ĂŒber 1206 Optimizer-LĂ€ufe bit-identisch.

Die Suche selbst bleibt auf dem Server. Die App bekommt nur die BewertungshĂ€lfte, denn Bibliotheken fĂŒr paralleles Suchen und HTTP haben auf dem Handy nichts verloren. Deshalb ist das Crate in zwei Teile getrennt, und der Compiler sorgt dafĂŒr, dass sich das nicht wieder vermischt. Wie Serverpod dazu passt, habe ich in meinem Vergleich zu Supabase und Firebase beschrieben.

Meine Sicht: Reihenfolge schlÀgt Technik

Ich baue Apps mit Flutter, und fĂŒr 95 Prozent meiner Projekte war Performance nie der Grund fĂŒr Probleme. Das ist meine Erfahrung, kein Zitat aus den Talks. Die Reihenfolge, die ich daraus mitnehme:

  1. LĂ€uft es auf einem echten, Ă€lteren GerĂ€t flĂŒssig? Wenn ja, hör auf.
  2. Wenn nicht: Profile-Build, DevTools. Ist es die UI-Thread-Arbeit oder die Raster-Seite?
  3. Raster-Seite? Blur, Opacity, ShaderMask, ein vergessener Ticker. Hier liegen fĂŒr normale Apps die meisten Gewinne.
  4. UI-Thread bei echter Schwerarbeit? Erst Isolates, dann ein besseres Paket, dann erst FFI.
  5. Rust nur bei bestĂ€tigtem Flaschenhals wie Bild-, Audio- oder Videoverarbeitung, wo es ein ausgereiftes natives Ökosystem gibt.

Spannend finde ich, dass KI-Agenten die HĂŒrde fĂŒr Schritt 5 senken. Der Speaker sagt selbst, dass man so etwas heute mit einfachen Anweisungen delegieren kann. Das passt zu dem, was ich in KI in der App-Entwicklung beschrieben habe. Aber: Wer FFI leichter baut, muss es trotzdem warten. Jede native AbhĂ€ngigkeit ist ein weiterer Build, den du pro Plattform im Blick behalten musst.

Und noch etwas: Der Vergleich native App vs. Cross-Platform wird oft gefĂŒhrt, als wĂ€re Performance der Knackpunkt. Beide Talks zeigen, dass du mit Flutter auch den nativen Weg nehmen kannst, wenn du ihn brauchst. Das nimmt dem Argument die SchĂ€rfe. Den direkten Vergleich zweier Frameworks findest du in Flutter vs React Native 2026.

Fazit

Flutter ist in den allermeisten FĂ€llen schnell genug. Wenn nicht, ist die Ursache oft kein Sprachproblem, sondern ein Frame-Problem: zu viele Frames, zu viele Passes.

Rust per FFI ist ein starkes Werkzeug fĂŒr echte Rechenlast, im Talk bis zu Faktor 100 bis 1000 bei Bildern. Aber es ist die letzte Stufe, nicht die erste. Erst messen, dann bauen.


HĂ€ufig gestellte Fragen (FAQ)


Dieser Artikel wurde inspiriert von zwei Talks auf der FlutterCon 2026: ĂŒber Rust und FFI fĂŒr Bildverarbeitung mit Pixer sowie ĂŒber GPU-Kosten von Blur und Tickern in Flutter.

Let's Connect! 🚀

Ready fĂŒr dein nĂ€chstes Projekt?
Lass uns durchstarten!

Lass uns starten

ErzĂ€hl mir von deiner Vision – schreib mir einfach per Mail oder ĂŒber das Kontaktformular. Lass uns gemeinsam deinen digitalen Erfolg planen! đŸ’Ș