# Flutter-Performance-Optimierung 2026: Impeller, Rebuilds und Best Practices > Wie Flutter-Apps 2026 konstante 60 oder 120 fps halten: mit Impeller, disziplinierten Widget-Rebuilds, RepaintBoundary und DevTools-Profiling. - Published: 2026-07-04 - Updated: 2026-07-07 - Author: SharpSkill - Tags: flutter, performance, impeller, best-practices, dart, mobile - Reading time: 9 min --- Die Optimierung der Flutter-Performance im Jahr 2026 beruht auf zwei Veränderungen, die den Bau flüssiger Apps neu geprägt haben: Die Rendering-Engine Impeller ist auf iOS und Android inzwischen standardmäßig aktiv, und das Rebuild-Modell des Frameworks belohnt Entwickler, die Widget-Bäume klein und unveränderlich halten. Teams, die beide Hebel nutzen, halten konstante 60 oder 120 fps, ohne jeden Frame von Hand zu justieren. > **Die wirkungsvollste Änderung** > > Der größte Gewinn in den meisten Flutter-Apps besteht darin, `const`-Konstruktoren hinzuzufügen und große `build`-Methoden aufzuteilen, damit ein `setState`-Aufruf nur einen einzigen Button neu zeichnet statt eines ganzen Bildschirms. ## Warum Impeller die Performance-Basislinie von Flutter neu setzt Impeller ist die Rendering-Engine von Flutter und hat Skia als Standard auf den mobilen Plattformen abgelöst. Der Unterschied zählt vor allem in den ersten Sekunden einer Animation. Skia kompilierte seine Shader zur Laufzeit, beim ersten Auftreten eines Effekts auf dem Bildschirm, was das berüchtigte Ruckeln beim ersten Durchlauf erzeugte, für das Flutter-Apps kritisiert wurden. Impeller kompiliert diese Shader vorab, während des Builds, sodass der allererste Frame einer Animation genauso flüssig ist wie der hundertste. Impeller zielt außerdem direkt auf moderne Grafik-APIs: Metal auf iOS und Vulkan auf Android, mit einem OpenGL-Rückfall für ältere Android-Hardware. Die [Dokumentation der Impeller-Engine auf GitHub](https://github.com/flutter/flutter/blob/master/docs/engine/impeller/README.md) beschreibt die Rendering-Architektur und die Matrix der Plattformunterstützung. | Aspekt | Skia (veraltet) | Impeller (Standard 2026) | |--------|---------------|-------------------------| | Shader-Kompilierung | Zur Laufzeit, verursacht Ruckeln beim ersten Durchlauf | Vorab, beim Build | | iOS-Backend | OpenGL / Metal | Metal | | Android-Backend | OpenGL | Vulkan, OpenGL-Rückfall | | Flüssigkeit der ersten Animation | Gelegentliches Ruckeln | Konstant | Für die meisten Projekte ist die Migration kostenlos: Aktuelle Flutter-Versionen aktivieren Impeller automatisch, und die alten Workarounds zum Shader-Warmup (`--purge-persistent-cache`, gebündelte `.sksl`-Dateien) werden nicht mehr benötigt und können gelöscht werden. Auch benutzerdefinierte `FragmentProgram`-Shader, die gegen die von Flutter unterstützte GLSL-Teilmenge geschrieben sind, funktionieren weiter, da Impeller sie über dieselbe Offline-Pipeline kompiliert wie die eigenen Effekte des Frameworks. Zu prüfen, welche Engine eine App verwendet, kostet eine Zeile: Ein Debug-Build gibt beim Start das aktive Backend aus, und der DevTools-Inspector meldet es in den Rendering-Details der App. Sobald Impeller bestätigt ist, wandert die restliche Performance-Arbeit hinauf in die Widget-Schicht, wo das Framework weit häufiger neu baut, als sich der Bildschirm tatsächlich ändert. Genau darauf konzentriert sich der Rest dieses Leitfadens, denn eine schnelle Engine kann einen Widget-Baum nicht retten, der sich hunderte Male pro Sekunde neu zeichnet. ## Flutter-Rebuilds mit const und Widget-Aufteilung reduzieren Jedes `setState` markiert sein Widget als veraltet und führt `build` für dieses Element und seinen Teilbaum erneut aus. Der Trick besteht darin, diesen Teilbaum so schmal wie möglich zu halten. Zwei Gewohnheiten leisten den Großteil der Arbeit: statische Widgets als `const` zu markieren und den Zustand nach unten zu schieben, sodass der zustandsbehaftete Teil klein bleibt. Ein `const`-Widget wird zu einer einzigen Instanz kanonisiert, sodass Flutter beim Rebuild eines Elternteils die identische Referenz vergleicht, keine Änderung erkennt und das Neuzeichnen dieses Zweigs komplett überspringt. ```dart // product_screen.dart // A cart update repaints the badge, not the header. class ProductScreen extends StatefulWidget { const ProductScreen({super.key}); @override State createState() => _ProductScreenState(); } class _ProductScreenState extends State { int _cartCount = 0; @override Widget build(BuildContext context) { return Column( children: [ const ProductHeader(), // const: never rebuilds on setState CartBadge(count: _cartCount), // only this subtree rebuilds FilledButton( onPressed: () => setState(() => _cartCount++), child: const Text('Add to cart'), ), ], ); } } ``` Den Lint `prefer_const_constructors` in der `analysis_options.yaml` zu aktivieren macht aus dieser Disziplin eine Compiler-Prüfung, sodass `const`-Gelegenheiten nie stillschweigend verloren gehen. Die verwandte Regel `prefer_const_literals_to_create_immutables` erweitert dieselbe Garantie auf Listen und Maps, die an Widgets übergeben werden. Widget-Keys verdienen hier ebenfalls Erwähnung. Wenn eine Liste umsortiert wird oder ein Widget denselben Typ behält, aber seine Identität ändert, lässt ein `ValueKey` oder `ObjectKey` Flutter das alte Element der richtigen neuen Widget-Instanz zuordnen, statt den Zustand abzureißen und neu aufzubauen. Fehlende Keys sind eine häufige Ursache für verlorene Scroll-Positionen und zurückgesetzte Animationen in dynamischen Listen, und die dadurch verursachte zusätzliche Rebuild-Arbeit wird leicht übersehen. ## Rebuilds auf die geänderten Daten begrenzen Wenn der Zustand weit oben im Baum liegt, baut sich selbst ein gut aufgeteiltes Layout viel neu auf. Die Lösung besteht darin, einen Wert zu senden und nur ein einziges Widget darauf hören zu lassen. Flutter liefert `ValueNotifier` und `ValueListenableBuilder` genau dafür, ohne dass ein externes Paket nötig ist. ```dart // article_list.dart // A scroll-to-top button that rebuilds alone, not the whole page. class ArticleList extends StatefulWidget { const ArticleList({super.key}); @override State createState() => _ArticleListState(); } class _ArticleListState extends State { final _showFab = ValueNotifier(false); final _controller = ScrollController(); @override void initState() { super.initState(); // Update the notifier, not setState: the list never rebuilds _controller.addListener(() => _showFab.value = _controller.offset > 400); } @override void dispose() { _showFab.dispose(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: ListView.builder( controller: _controller, itemCount: 200, itemBuilder: (_, i) => ListTile(title: Text('Item $i')), ), floatingActionButton: ValueListenableBuilder( valueListenable: _showFab, builder: (_, visible, __) => visible ? FloatingActionButton( onPressed: () => _controller.jumpTo(0), child: const Icon(Icons.arrow_upward), ) : const SizedBox.shrink(), ), ); } } ``` Bibliotheken zur Zustandsverwaltung verallgemeinern dieselbe Idee. Riverpods `ref.watch(provider.select(...))` und das `Selector`-Widget von Provider bauen beide nur dann neu auf, wenn sich ein ausgewählter Teil des Zustands ändert, was auf datenlastigen Bildschirmen den Ausschlag zwischen flüssig und ruckelig gibt. Die Abwägungen zwischen diesen Bibliotheken behandelt der Leitfaden zur [Flutter-Zustandsverwaltung 2026](/blog/flutter/flutter-state-management-2026-riverpod-bloc-getx). > **Auf den Rebuild-Umfang achten** > > Ein `setState`-Aufruf nahe der Wurzel eines Bildschirms oder das Einhüllen einer ganzen Seite in einen `Consumer` auf oberster Ebene zeichnet bei jeder Änderung still hunderte Widgets neu. Profilieren Sie mit dem Rebuild-Zähler in den DevTools, bevor Sie annehmen, ein Bildschirm sei günstig. ## Teure Repaints mit RepaintBoundary isolieren Rebuild und Repaint sind getrennte Phasen. Ein Widget kann den Rebuild überspringen und sich dennoch neu zeichnen, weil ein unruhiger Nachbar seine Ebene teilt. `RepaintBoundary` gibt einem Teilbaum eine eigene Ebene, sodass ein animiertes Diagramm statische Karten nicht zwingen kann, jeden Frame neu zu zeichnen. ```dart // dashboard.dart // The live chart repaints on its own layer, cards stay untouched. class Dashboard extends StatelessWidget { const Dashboard({required this.ticker, super.key}); final Stream ticker; @override Widget build(BuildContext context) { return Column( children: [ // SummaryCards is static, so isolate it from the animation below const RepaintBoundary(child: SummaryCards()), RepaintBoundary( child: StreamBuilder( stream: ticker, builder: (_, snap) => LivePriceChart(value: snap.data ?? 0), ), ), ], ); } } ``` RepaintBoundary ist nicht kostenlos: Jede Instanz belegt eine Ebene, sodass es mehr schadet als nützt, es überall zu verstreuen. Man greift dazu bei wirklich animierten Bereichen und langen Scroll-Listen und bestätigt den Gewinn in der Timeline. Die [RepaintBoundary-API-Referenz](https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html) dokumentiert, wie das Debug-Repaint-Overlay hervorhebt, welche Ebenen tatsächlich neu gezeichnet werden. ## Die build-Methode günstig und ohne Seiteneffekte halten Eine `build`-Methode läuft bei jedem Rebuild, also vervielfacht sich alles Teure darin. Die Regel lautet: `build` soll Zustand lesen und Widgets zurückgeben, mehr nicht. Einen `ScrollController`, `AnimationController` oder ein `Future` innerhalb von `build` zu erzeugen, legt es bei jedem Frame neu an und verliert die vorherige Instanz. Diese Objekte gehören in `initState` oder in ein `late final`-Feld, das in `dispose` freigegeben wird. Schwere synchrone Arbeit ist die zweite Falle. JSON zu parsen, eine große Liste zu filtern oder Daten innerhalb von `build` zu formatieren, führt diese Arbeit bei jedem Repaint erneut aus, selbst wenn sich die zugrunde liegenden Daten nicht geändert haben. Berechnen Sie das Ergebnis einmal, wenn sich die Eingabe ändert, und speichern Sie es zwischen, oder verschieben Sie es in einen `FutureBuilder`, damit das Framework nicht einen Frame lang darauf wartet. Für aus anderen Werten abgeleitete Werte memoisieren Sie sie hinter einem Getter, der nur bei geänderten Abhängigkeiten neu berechnet. Das `Opacity`-Widget ist ein konkreter Übeltäter, den man benennen sollte. Einen Teilbaum in `Opacity` zu hüllen, erzwingt ein `saveLayer` außerhalb des Bildschirms, eine der teuersten Operationen der Pipeline. Für eine Überblendung sind `AnimatedOpacity` oder eine `FadeTransition` günstiger, und für eine feste Tönung vermeidet das Anwenden der Farbe auf Paint-Ebene über eine `foregroundDecoration` oder einen Shader die Ebene ganz. Dieselbe Vorsicht gilt für `ClipPath` und `ClipRRect` mit Antialiasing auf großen Flächen, die im Hintergrund ebenfalls ein `saveLayer` auslösen. Bevorzugen Sie schließlich `const`-Sammlungen und Callbacks, die nicht bei jedem Build neue Closures einfangen. Ein als Standard übergebenes `const []` und eine Methodenreferenz statt einer Inline-Lambda halten die Gleichheitsprüfungen von Widgets günstig, was den zuvor beschriebenen `const`-Kurzschluss überhaupt erst auslösen lässt. ## Lange Listen und Bilder effizient aufbauen Lange Feeds und Bildraster sind die Stelle, an der naiver Flutter-Code Frames verliert. Zwei Regeln decken die meisten Fälle ab: Zeilen faul aufbauen und Bilder in der Größe dekodieren, in der sie angezeigt werden, statt in ihrer Quellauflösung. ```dart // feed.dart // Lazy rows plus right-sized image decoding keep scrolling at 60fps. class Feed extends StatelessWidget { const Feed({required this.posts, super.key}); final List posts; @override Widget build(BuildContext context) { return ListView.builder( itemCount: posts.length, // build on demand, not all at once cacheExtent: 600, // pre-build past the viewport edge itemBuilder: (context, index) { final post = posts[index]; return ListTile( leading: Image.network( post.thumbnailUrl, width: 48, height: 48, cacheWidth: 96, // decode at display size, save memory ), title: Text(post.title), ); }, ); } } ``` Ein Bild in voller Auflösung mit 3000 Pixeln, das in einen 48-Pixel-Avatar gequetscht wird, verschwendet bei jedem einzelnen Speicher und Dekodierzeit. `cacheWidth` zu setzen, weist die Engine an, in der Zielgröße zu dekodieren, was allein den Speicherbedarf einer Liste um eine Größenordnung senken kann. Die [Best Practices zur Performance](https://docs.flutter.dev/perf/best-practices) von Flutter bündeln weitere Hinweise zu `Opacity`, `saveLayer` und Clip-Nutzung, die demselben Prinzip folgen. ## Flutter-Optimierung 2026 mit DevTools messen Jede obige Regel sollte verifiziert und nicht angenommen werden. Flutter DevTools ist die verlässliche Quelle. Das Performance-Overlay (`P` in der Debug-Konsole oder `flutter run --profile`) zeichnet zwei Graphen, UI und Raster, und jeder Balken, der die grüne Linie überschreitet, ist ein verlorener Frame. Die Timeline-Ansicht ordnet dann jeden langsamen Frame einem bestimmten `build`-, Layout- oder Paint-Aufruf zu. Profilieren Sie immer im `--profile`-Modus auf einem physischen Gerät. Debug-Builds führen unoptimiertes Dart aus und blähen jede Messung auf, sodass ein Bildschirm, der im Debug ruckelt, kompiliert oft einwandfrei läuft. Der [DevTools-Performance-Leitfaden](https://docs.flutter.dev/tools/devtools/performance) führt durch das Lesen des Frame-Diagramms und das Aufspüren von Ruckelquellen, und dieselbe Profiling-Routine taucht in Vorstellungsgesprächen über [Flutter-Animationen und Rendering](/technologies/flutter/interview-questions/animations) auf. > **Eine schnelle Profiling-Schleife** > > Im Profile-Modus starten, die ruckelige Interaktion reproduzieren, die Timeline öffnen und die Frames nach Dauer sortieren. Der schlechteste Frame verweist fast immer auf einen einzigen überdimensionierten Rebuild oder ein nicht zwischengespeichertes Bild, was weit schneller ist als Raten. Der [Flutter-Technologie-Hub](/technologies/flutter) verknüpft die Themen Zustandsverwaltung, Testing und Rendering, die eine produktionsreife Performance-Strategie abrunden. ## Fazit Flutter-Performance im Jahr 2026 geht weniger um Mikro-Tricks als um eine kurze Liste konsequent angewandter Gewohnheiten: - Mit Impeller ausliefern und alten Shader-Warmup-Code löschen; das Problem des Ruckelns im ersten Frame ist auf Engine-Ebene gelöst. - Statische Widgets als `const` markieren und dies mit dem Lint `prefer_const_constructors` erzwingen. - Zustandsbehaftete Widgets klein aufteilen, sodass jedes `setState` den schmalstmöglichen Teilbaum neu zeichnet. - Sich ändernde Werte über `ValueListenableBuilder` oder eine `select`-artige API senden, statt ganze Bildschirme neu aufzubauen. - Wirklich animierte Bereiche in ein `RepaintBoundary` hüllen, aber messen, bevor man mehr hinzufügt. - Listen mit `ListView.builder` aufbauen und Bilder mit `cacheWidth` in Anzeigegröße dekodieren. - Im `--profile`-Modus auf echter Hardware profilieren und DevTools, nicht die Intuition, entscheiden lassen, was als Nächstes zu optimieren ist. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/flutter/flutter-performance-optimization-2026-impeller-rebuilds