# Optymalizacja wydajności Fluttera w 2026: Impeller, przebudowy i dobre praktyki > Jak utrzymać aplikacje Flutter na stałych 60 lub 120 fps w 2026 dzięki Impellerowi, zdyscyplinowanym przebudowom widgetów, RepaintBoundary i profilowaniu w DevTools. - Published: 2026-07-04 - Updated: 2026-07-07 - Author: SharpSkill - Tags: flutter, performance, impeller, best-practices, dart, mobile - Reading time: 9 min --- Optymalizacja wydajności Fluttera w 2026 roku opiera się na dwóch zmianach, które przekształciły sposób budowania płynnych aplikacji: silnik renderujący Impeller jest teraz domyślnie włączony na iOS i Androidzie, a model przebudowy frameworka nagradza deweloperów utrzymujących drzewa widgetów małe i niezmienne. Zespoły korzystające z obu dźwigni utrzymują stałe 60 lub 120 fps bez ręcznego dostrajania każdej klatki. > **Zmiana o największym wpływie** > > Największy zysk w większości aplikacji Flutter polega na dodaniu konstruktorów `const` i podziale dużych metod `build`, tak aby wywołanie `setState` przerysowywało jeden przycisk zamiast całego ekranu. ## Dlaczego Impeller na nowo wyznacza punkt odniesienia wydajności Fluttera Impeller jest silnikiem renderującym Fluttera i zastąpił Skię jako domyślny na platformach mobilnych. Różnica ma znaczenie przede wszystkim w pierwszych sekundach animacji. Skia kompilowała swoje shadery w locie, przy pierwszym pojawieniu się danego efektu na ekranie, co powodowało osławione zacinanie przy pierwszym uruchomieniu, za które krytykowano aplikacje Flutter. Impeller kompiluje te shadery z wyprzedzeniem, podczas budowania, dzięki czemu pierwsza klatka animacji jest tak samo płynna jak setna. Impeller celuje też bezpośrednio w nowoczesne API graficzne: Metal na iOS i Vulkan na Androidzie, z awaryjnym OpenGL dla starszego sprzętu z Androidem. [Dokumentacja silnika Impeller na GitHubie](https://github.com/flutter/flutter/blob/master/docs/engine/impeller/README.md) opisuje architekturę renderowania i macierz wsparcia platform. | Aspekt | Skia (dziedzictwo) | Impeller (domyślny 2026) | |--------|---------------|-------------------------| | Kompilacja shaderów | W czasie działania, powoduje zacinanie przy pierwszym uruchomieniu | Z wyprzedzeniem, przy budowaniu | | Backend iOS | OpenGL / Metal | Metal | | Backend Androida | OpenGL | Vulkan, awaryjny OpenGL | | Płynność pierwszej animacji | Sporadyczne zacinanie | Stała | Dla większości projektów migracja jest darmowa: najnowsze wydania Fluttera włączają Impeller automatycznie, a stare obejścia rozgrzewania shaderów (`--purge-persistent-cache`, dołączane pliki `.sksl`) nie są już potrzebne i można je usunąć. Niestandardowe shadery `FragmentProgram` napisane z użyciem obsługiwanego przez Flutter podzbioru GLSL również działają dalej, ponieważ Impeller kompiluje je tym samym potokiem offline co własne efekty frameworka. Sprawdzenie, na jakim silniku działa aplikacja, zajmuje jedną linię: kompilacja debug wypisuje aktywny backend przy starcie, a inspektor DevTools raportuje go w szczegółach renderowania aplikacji. Po potwierdzeniu Impellera pozostała praca nad wydajnością przenosi się w górę, do warstwy widgetów, gdzie framework przebudowuje znacznie częściej, niż ekran faktycznie się zmienia. Właśnie na tym skupia się reszta tego przewodnika, bo szybki silnik nie uratuje drzewa widgetów, które przerysowuje się setki razy na sekundę. ## Ograniczanie przebudów Fluttera dzięki const i podziałowi widgetów Każde `setState` oznacza swój widget jako nieaktualny i ponownie uruchamia `build` dla tego elementu i jego poddrzewa. Sztuką jest utrzymanie tego poddrzewa jak najwęższego. Dwa nawyki wykonują większość pracy: oznaczanie statycznych widgetów jako `const` i spychanie stanu w dół, aby część ze stanem pozostawała mała. Widget `const` jest kanonizowany do jednej instancji, więc gdy rodzic się przebudowuje, Flutter porównuje identyczną referencję, nie widzi zmiany i całkowicie pomija przerysowanie tej gałęzi. ```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'), ), ], ); } } ``` Włączenie linta `prefer_const_constructors` w `analysis_options.yaml` zamienia tę dyscyplinę w kontrolę kompilatora, dzięki czemu okazje do użycia `const` nigdy po cichu nie znikają. Powiązana reguła `prefer_const_literals_to_create_immutables` rozszerza tę samą gwarancję na listy i mapy przekazywane do widgetów. Klucze widgetów również zasługują tu na wzmiankę. Gdy lista zmienia kolejność lub widget zachowuje ten sam typ, ale zmienia tożsamość, `ValueKey` lub `ObjectKey` pozwala Flutterowi dopasować stary element do właściwej nowej instancji widgetu zamiast burzyć i odtwarzać stan. Brak kluczy to częsta przyczyna utraty pozycji przewijania i resetowania animacji w listach dynamicznych, a powodowaną przez nie dodatkową pracę przebudowy łatwo przeoczyć. ## Zawężanie przebudów do danych, które się zmieniły Gdy stan żyje wysoko w drzewie, nawet dobrze podzielony układ i tak przebudowuje się dużo. Rozwiązaniem jest rozgłaszanie wartości i pozwolenie jednemu widgetowi jej nasłuchiwać. Flutter dostarcza `ValueNotifier` i `ValueListenableBuilder` dokładnie do tego, bez potrzeby żadnego zewnętrznego pakietu. ```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(), ), ); } } ``` Biblioteki do zarządzania stanem uogólniają tę samą ideę. `ref.watch(provider.select(...))` z Riverpod oraz widget `Selector` z Provider przebudowują się tylko wtedy, gdy zmienia się wybrany wycinek stanu, co przesądza o różnicy między płynnością a zacinaniem na ekranach bogatych w dane. Kompromisy między tymi bibliotekami omawia przewodnik po [zarządzaniu stanem we Flutterze w 2026](/blog/flutter/flutter-state-management-2026-riverpod-bloc-getx). > **Uważaj na zasięg przebudowy** > > Wywołanie `setState` blisko korzenia ekranu albo owinięcie całej strony w `Consumer` na najwyższym poziomie po cichu przerysowuje setki widgetów przy każdej zmianie. Profiluj licznikiem przebudów w DevTools, zanim założysz, że ekran jest tani. ## Izolowanie kosztownych przerysowań za pomocą RepaintBoundary Przebudowa i przerysowanie to osobne etapy. Widget może pominąć przebudowę, a mimo to się przerysować, ponieważ hałaśliwy sąsiad dzieli jego warstwę. `RepaintBoundary` nadaje poddrzewu własną warstwę, dzięki czemu animowany wykres nie może zmusić statycznych kart do przerysowywania w każdej klatce. ```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 nie jest darmowy: każdy alokuje warstwę, więc rozsypywanie go wszędzie szkodzi bardziej, niż pomaga. Warto sięgać po niego wokół naprawdę animowanych obszarów i długich przewijanych list, potwierdzając zysk na osi czasu. [Dokumentacja API RepaintBoundary](https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html) opisuje, jak debugowa nakładka przerysowań wyróżnia warstwy faktycznie przerysowywane. ## Utrzymywanie metody build taniej i bez skutków ubocznych Metoda `build` wykonuje się przy każdej przebudowie, więc wszystko, co kosztowne w środku, się mnoży. Reguła brzmi: `build` powinien odczytywać stan i zwracać widgety, nic więcej. Utworzenie `ScrollController`, `AnimationController` lub `Future` wewnątrz `build` odtwarza je przy każdej klatce i porzuca poprzednią instancję. Te obiekty należą do `initState` albo do pola `late final`, zwalnianego w `dispose`. Ciężka praca synchroniczna to druga pułapka. Parsowanie JSON-a, filtrowanie dużej listy czy formatowanie dat wewnątrz `build` ponawia tę pracę przy każdym przerysowaniu, nawet gdy dane bazowe się nie zmieniły. Oblicz wynik raz, gdy zmienia się wejście, i zapisz go w pamięci podręcznej, albo przenieś go do `FutureBuilder`, aby framework nie blokował klatki, czekając na niego. Wartości pochodne od innych wartości memoizuj za getterem, który przelicza się tylko, gdy zmieniają się jego zależności. Widget `Opacity` to konkretny winowajca warty nazwania. Owinięcie poddrzewa w `Opacity` wymusza `saveLayer` poza ekranem, jedną z najkosztowniejszych operacji w potoku. Dla animacji zanikania `AnimatedOpacity` lub `FadeTransition` są tańsze, a dla stałego odcienia zastosowanie koloru na poziomie malowania przez `foregroundDecoration` lub shader całkowicie unika warstwy. Ta sama ostrożność dotyczy `ClipPath` i `ClipRRect` z antyaliasingiem na dużych powierzchniach, które za kulisami także wyzwalają `saveLayer`. Na koniec preferuj kolekcje `const` i callbacki, które nie przechwytują świeżych domknięć przy każdym budowaniu. Przekazane jako domyślne `const []` oraz referencja do metody zamiast wbudowanej lambdy utrzymują tanie porównania równości widgetów, co pozwala opisanemu wcześniej skrótowi `const` faktycznie zadziałać. ## Wydajne budowanie długich list i obrazów Długie feedy i siatki obrazów to miejsce, gdzie naiwny kod Fluttera gubi klatki. Dwie reguły pokrywają większość przypadków: budować wiersze leniwie i dekodować obrazy w rozmiarze, w jakim się wyświetlają, a nie w rozdzielczości źródłowej. ```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), ); }, ); } } ``` Obraz w pełnej rozdzielczości 3000 pikseli wciśnięty w awatar o szerokości 48 pikseli marnuje pamięć i czas dekodowania przy każdym z nich. Ustawienie `cacheWidth` mówi silnikowi, aby dekodował w rozmiarze docelowym, co samo w sobie może zmniejszyć zużycie pamięci listy o rząd wielkości. [Najlepsze praktyki wydajności](https://docs.flutter.dev/perf/best-practices) Fluttera zbierają dalsze wskazówki dotyczące `Opacity`, `saveLayer` i użycia przycinania, które podążają za tą samą zasadą. ## Mierzenie optymalizacji Fluttera w 2026 za pomocą DevTools Każdą powyższą regułę należy zweryfikować, a nie zakładać. Flutter DevTools jest źródłem prawdy. Nakładka wydajności (`P` w konsoli debug albo `flutter run --profile`) rysuje dwa wykresy, UI i raster, a każdy słupek przekraczający zieloną linię to zgubiona klatka. Widok osi czasu przypisuje potem każdą wolną klatkę do konkretnego wywołania `build`, układu lub malowania. Zawsze profiluj w trybie `--profile` na fizycznym urządzeniu. Kompilacje debug uruchamiają niezoptymalizowany Dart i zawyżają każdy pomiar, więc ekran, który zacina się w debug, często działa idealnie po skompilowaniu. [Przewodnik po wydajności DevTools](https://docs.flutter.dev/tools/devtools/performance) prowadzi przez odczytywanie wykresu klatek i wykrywanie źródeł zacięć, a ten sam nawyk profilowania pojawia się na rozmowach o [animacjach i renderowaniu we Flutterze](/technologies/flutter/interview-questions/animations). > **Szybka pętla profilowania** > > Uruchom w trybie profile, odtwórz zacinającą się interakcję, otwórz oś czasu i posortuj klatki według czasu trwania. Najgorsza klatka niemal zawsze wskazuje na pojedynczą zbyt dużą przebudowę lub niebuforowany obraz, co jest znacznie szybsze niż zgadywanie. [Centrum technologii Flutter](/technologies/flutter) łączy tematy zarządzania stanem, testowania i renderowania, które dopełniają produkcyjną strategię wydajności. ## Podsumowanie Wydajność Fluttera w 2026 to mniej mikrosztuczki, a bardziej krótka lista nawyków stosowanych konsekwentnie: - Dostarczaj na Impellerze i usuwaj przestarzały kod rozgrzewania shaderów; problem zacinania pierwszej klatki jest rozwiązany na poziomie silnika. - Oznaczaj statyczne widgety jako `const` i wymuszaj to lintem `prefer_const_constructors`. - Dziel widgety ze stanem na małe jednostki, aby każde `setState` przerysowywało najwęższe możliwe poddrzewo. - Rozgłaszaj zmieniające się wartości przez `ValueListenableBuilder` lub API typu `select` zamiast przebudowywać całe ekrany. - Owijaj naprawdę animowane obszary w `RepaintBoundary`, ale mierz, zanim dodasz ich więcej. - Buduj listy z `ListView.builder` i dekoduj obrazy w rozmiarze wyświetlania za pomocą `cacheWidth`. - Profiluj w trybie `--profile` na prawdziwym sprzęcie i pozwól DevTools, a nie intuicji, zdecydować, co optymalizować dalej. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/flutter/flutter-performance-optimization-2026-impeller-rebuilds