---
title: "Ist Flutter schnell genug? Wann du Rust brauchst und wie du es herausfindest"
description: "Flutter ist selten das Problem. Zwei Talks von der FlutterCon 2026 zeigen, wann Rust per FFI 100x bringt und wann ein Ticker deine GPU grillt. Plus: wie du den Engpass findest."
canonical_url: "https://robocitrus.com/blog/flutter-performance-wann-rust-ffi-und-profiling"
---

> Index of every page on this site: [llms.txt](https://robocitrus.com/llms.txt)

## 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](https://docs.flutter.dev/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](https://robocitrus.com/projects/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:

<table>
<thead>
  <tr>
    <th>
      Implementierung
    </th>
    
    <th>
      Median
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Dart-Port (Deep-Variante)
    </td>
    
    <td>
      1031,6 ms
    </td>
  </tr>
  
  <tr>
    <td>
      Dart-Port (Basis-Variante)
    </td>
    
    <td>
      278,8 ms
    </td>
  </tr>
  
  <tr>
    <td>
      JS-Original, Node
    </td>
    
    <td>
      103,7 ms
    </td>
  </tr>
  
  <tr>
    <td>
      JS, Bun
    </td>
    
    <td>
      61,9 ms
    </td>
  </tr>
  
  <tr>
    <td>
      Rust, ein Thread
    </td>
    
    <td>
      7,7 ms
    </td>
  </tr>
  
  <tr>
    <td>
      Rust, alle Kerne
    </td>
    
    <td>
      1,1 ms
    </td>
  </tr>
</tbody>
</table>

Ü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](https://github.com/fzyzcjy/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](https://robocitrus.com/blog/serverpod-vs-supabase-vs-firebase) beschrieben.

## Meine Sicht: Reihenfolge schlägt Technik

Ich baue Apps mit [Flutter](https://robocitrus.com/blog/warum-ich-flutter-liebe), 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](https://robocitrus.com/blog/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](https://robocitrus.com/blog/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](https://robocitrus.com/blog/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)

<accordion>
<accordion-item icon="i-lucide-circle-help" label="Ist Flutter für performance-kritische Apps geeignet?">

Meistens ja. Die Talks zeigen, dass die Engpässe fast immer an konkreten Stellen sitzen: schwere Berechnungen oder teure Render-Effekte. Beides lässt sich gezielt lösen, ohne das Framework zu wechseln.

</accordion-item>

<accordion-item icon="i-lucide-circle-help" label="Wann lohnt sich Rust per FFI in Flutter?">

Wenn du einen gemessenen Flaschenhals in Dart hast und es eine ausgereifte native Lösung gibt, etwa bei Bild-, Audio- oder Videoverarbeitung oder Kryptografie. Bei sehr vielen kleinen Aufrufen kann der Übergang selbst teurer sein als der Gewinn.

</accordion-item>

<accordion-item icon="i-lucide-circle-help" label="Brauche ich Flutter Rust Bridge oder reicht manuelles FFI?">

Der Speaker im Talk empfiehlt Einsteigern, erst den manuellen Weg mit `cbindgen` und `ffigen` zu lernen. Dann verstehst du, was die Bridge im Hintergrund für dich erledigt, und kannst sie bewusst einsetzen.

</accordion-item>

<accordion-item icon="i-lucide-circle-help" label="Warum verbraucht mein Flutter-Screen so viel Akku?">

Ein häufiger Grund ist ein aktiver Ticker oder eine Dauer-Animation, die Frames in voller Display-Rate anfordert. In Kombination mit Blur oder Offscreen-Opacity wird jeder dieser Frames teuer. Profile mit Xcode und DevTools zeigt dir, was wirklich läuft.

</accordion-item>

<accordion-item icon="i-lucide-circle-help" label="Wie finde ich heraus, was meine App ausbremst?">

Immer im Profile-Modus auf einem echten Gerät messen. DevTools für CPU und UI, auf iOS zusätzlich Metal System Trace und Frame Capture für die GPU-Seite. Erst wenn du den Engpass kennst, lohnt sich Optimieren.

</accordion-item>
</accordion>

---

*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.*

## Sitemap

See the full [sitemap](https://robocitrus.com/sitemap.md) for all pages.
