Impeller Flutter: Nowa era mobilnej grafiki

5/8/2025 Mobilne

Mateusz Kędziora

image

Hej programiści Fluttera! Chcecie, żeby Wasze aplikacje działały płynniej, miały lepszą grafikę i wykorzystywały nowoczesne API? To dobrze trafiliście! W tym artykule przyjrzymy się Impellerowi – nowemu silnikowi renderującemu w Flutterze, który ma za zadanie zrewolucjonizować sposób, w jaki aplikacje są wyświetlane na ekranach.

Co to jest Impeller i dlaczego powstał?

Do niedawna Flutter polegał na silniku Skia do renderowania grafiki. Skia to bardzo wszechstronne i dojrzałe rozwiązanie, ale ma swoje ograniczenia. Przede wszystkim, Skia kompiluje shading w momencie uruchomienia aplikacji. To, z kolei, prowadzi do janków, czyli krótkotrwałych spadków wydajności, które są szczególnie uciążliwe w animacjach. Wyobraź sobie, że budujesz skomplikowaną animację, ale co jakiś czas widzisz przeskok. Frustrujące, prawda?

Impeller został stworzony, aby rozwiązać ten problem i zapewnić:

  • Przewidywalną wydajność: Kompilacja shaderów odbywa się wcześniej – podczas budowania aplikacji (AOT – Ahead-Of-Time), a nie w trakcie jej działania. To eliminuje janki spowodowane kompilacją shaderów w locie.
  • Ulepszoną grafikę: Impeller został zaprojektowany z myślą o nowoczesnych API graficznych, takich jak Metal (iOS) i Vulkan (Android). Pozwala to na pełne wykorzystanie możliwości tych API i rendering bardziej skomplikowanych i efektownych efektów wizualnych.
  • Lepszą kontrolę: Impeller daje deweloperom większą kontrolę nad całym procesem renderowania.

Krótko mówiąc, Impeller to gruntowna przebudowa silnika renderującego, która ma na celu poprawę wydajności, jakości grafiki i kontroli deweloperów nad procesem renderowania w Flutterze.

Impeller vs. Skia: Porównanie

FunkcjaSkiaImpeller
Kompilacja shaderówJIT (Just-In-Time) – podczas działaniaAOT (Ahead-Of-Time) – podczas budowania
API GraficzneOpenGL ES (głównie)Metal, Vulkan
JankiPotencjalne (kompilacja shaderów)Zminimalizowane
WydajnośćZależna od urządzenia i złożoności grafikiBardziej przewidywalna i stabilna
KontrolaMniejszaWiększa

Jak widzisz, Impeller oferuje szereg zalet w porównaniu ze Skia, szczególnie w kontekście wydajności i nowoczesnych API graficznych.

Cele Impellera: Przewidywalna wydajność, ulepszona grafika i nowoczesne API

Główne cele Impellera to:

  1. Przewidywalna wydajność: Eliminacja janków i zapewnienie płynnych animacji, niezależnie od urządzenia i złożoności grafiki.
  2. Ulepszona grafika: Wykorzystanie nowoczesnych API graficznych (Metal, Vulkan) do renderingu bardziej skomplikowanych i efektownych efektów wizualnych.
  3. Obsługa nowoczesnych API graficznych: Pełne wykorzystanie możliwości Metal (iOS) i Vulkan (Android) do optymalizacji renderowania.
  4. Lepsza debugowalność: Ułatwienie diagnozowania problemów związanych z renderowaniem i optymalizacja wydajności.
  5. Rozszerzalność: Ułatwienie dodawania nowych funkcji i optymalizacji w przyszłości.

Dostępność Impellera na różnych platformach

Obecnie (stan na maj 2025) Impeller jest dostępny na następujących platformach:

  • iOS: W pełni obsługiwany i domyślnie włączony.
  • Android: W pełni obsługiwany i domyślnie włączony (zależnie od konfiguracji urządzenia i wersji Fluttera).
  • Web (CanvasKit): Planowana obsługa, ale na maj 2025 nie jest jeszcze domyślnie włączona.
  • macOS: Eksperymentalna obsługa. Może wymagać włączenia flagą konfiguracyjną.
  • Windows: Eksperymentalna obsługa. Może wymagać włączenia flagą konfiguracyjną.
  • Linux: Planowana obsługa.

Ważne: Dostępność i status włączenia Impellera mogą się zmieniać w kolejnych wersjach Fluttera. Zawsze sprawdzaj najnowszą dokumentację Fluttera, aby uzyskać aktualne informacje.

Włączanie i Wyłączanie Impellera

iOS:

Od Flutter 3.16, Impeller jest domyślnie włączony dla iOS. Nie ma potrzeby ręcznego włączania. Aby go wyłączyć (tylko w celach testowych lub debugowania), możesz dodać następujący klucz do pliku Info.plist w katalogu ios/Runner/:

<key>FLTEnableImpeller</key>
<false/>

Android:

Impeller na Androidzie jest domyślnie włączony, pod warunkiem spełnienia pewnych wymagań:

  • Urządzenie musi mieć Androida w wersji 10 (API level 29) lub nowszej.
  • Urządzenie musi obsługiwać Vulkan 1.1 lub nowszą.

Aby sprawdzić, czy Impeller jest włączony na Androidzie, możesz użyć polecenia:

adb shell getprop ro.gfx.driver.impeller

Jeśli polecenie zwróci 1, Impeller jest włączony.

Jeśli chcesz eksperymentalnie włączyć Impeller na Androidzie, nawet jeśli nie spełnia on wymagań domyślnych (uważaj, może to spowodować problemy!), możesz spróbować:

  1. Dodaj następujący kod do pliku android/app/src/main/AndroidManifest.xml w tagu <application>:

    <meta-data
        android:name="io.flutter.embedding.android.EnableImpeller"
        android:value="true" />
  2. Uruchom aplikację.

Ważne: Włączanie Impellera na urządzeniach, które nie spełniają minimalnych wymagań, może prowadzić do problemów z wydajnością lub stabilnością.

Web (CanvasKit):

Obecnie nie ma domyślnej obsługi Impellera w CanvasKit. Należy śledzić oficjalne komunikaty od zespołu Fluttera odnośnie przyszłej implementacji.

macOS i Windows:

Na tych platformach Impeller jest w fazie eksperymentalnej i może wymagać włączenia poprzez flagę konfiguracyjną podczas uruchamiania aplikacji. Dokładne instrukcje znajdziesz w dokumentacji Fluttera dotyczącej budowania aplikacji na te platformy. Zazwyczaj wygląda to tak:

flutter run --enable-impeller  # macOS
flutter run --enable-impeller  # Windows

Korzyści wydajnościowe: Płynniejsze animacje i redukcja zacięć

Główną korzyścią wynikającą z używania Impellera jest poprawa wydajności, a w szczególności:

  • Płynniejsze animacje: Eliminacja janków spowodowanych kompilacją shaderów w locie skutkuje płynniejszymi animacjami, co znacząco poprawia wrażenia użytkownika.
  • Redukcja zacięć: Ogólna poprawa wydajności renderowania prowadzi do zmniejszenia liczby zacięć i opóźnień, szczególnie w skomplikowanych interfejsach użytkownika.
  • Bardziej przewidywalna wydajność: Dzięki kompilacji shaderów AOT, wydajność aplikacji jest bardziej przewidywalna i mniej zależna od specyfikacji urządzenia.
  • Lepsza wydajność na starszych urządzeniach: Impeller może poprawić wydajność aplikacji na starszych urządzeniach, które mogą mieć problemy z wydajnym renderowaniem grafiki za pomocą Skia.

Przykład:

Wyobraź sobie, że masz złożoną animację przejścia między ekranami, która wykorzystuje wiele warstw i efektów wizualnych. Na urządzeniu z Skia możesz zauważyć sporadyczne janki podczas uruchamiania animacji po raz pierwszy. Z Impellerem animacja powinna działać płynniej i bardziej stabilnie, bez względu na to, ile razy ją uruchamiasz.

Rozwiązywanie problemów z Impellerem

Jeśli napotkasz problemy z Impellerem, oto kilka kroków, które możesz podjąć:

  1. Sprawdź, czy Impeller jest włączony: Upewnij się, że Impeller jest włączony na Twojej platformie (zgodnie z instrukcjami powyżej).
  2. Zaktualizuj Fluttera: Używaj najnowszej stabilnej wersji Fluttera. Nowe wersje często zawierają poprawki błędów i optymalizacje związane z Impellerem.
  3. Sprawdź logi: Przejrzyj logi aplikacji w poszukiwaniu błędów lub ostrzeżeń związanych z renderowaniem grafiki.
  4. Wyłącz Impeller (tymczasowo): Jeśli podejrzewasz, że Impeller powoduje problemy, możesz go tymczasowo wyłączyć (zgodnie z instrukcjami powyżej) i sprawdzić, czy problem zniknie. Jeśli tak, prawdopodobnie znalazłeś błąd związany z Impellerem.
  5. Użyj narzędzi debugowania grafiki: Użyj narzędzi debugowania grafiki (np. RenderDoc) aby zbadać proces renderowania i zidentyfikować potencjalne problemy.
  6. Przetestuj na różnych urządzeniach: Sprawdź, czy problem występuje tylko na konkretnym urządzeniu, czy na wszystkich urządzeniach.

Zgłaszanie błędów związanych z Impellerem

Jeśli zidentyfikujesz błąd związany z Impellerem, ważne jest, aby go zgłosić zespołowi Fluttera. Oto jak to zrobić:

  1. Stwórz minimalny przykład kodu: Spróbuj stworzyć minimalny przykład kodu, który odtwarza problem. Im prostszy przykład, tym łatwiej będzie zespołowi Fluttera zdiagnozować i naprawić błąd.
  2. Sprawdź istniejące zgłoszenia: Przed zgłoszeniem nowego błędu, sprawdź, czy ktoś inny nie zgłosił już tego samego problemu. Możesz przeszukać repozytorium Fluttera na GitHubie, używając słów kluczowych związanych z Twoim problemem.
  3. Zgłoś błąd na GitHubie: Jeśli nie znajdziesz istniejącego zgłoszenia, zgłoś nowy błąd w repozytorium Fluttera na GitHubie.
    • Podaj szczegółowy opis problemu.
    • Dołącz minimalny przykład kodu, który odtwarza problem.
    • Podaj informacje o swoim środowisku (wersja Fluttera, system operacyjny, urządzenie).
    • Dołącz logi aplikacji.

Im więcej informacji podasz, tym łatwiej będzie zespołowi Fluttera pomóc.

Przykład zgłoszenia błędu:

Tytuł: Jank podczas animacji przejścia na iOS z Impellerem

Opis: Podczas animacji przejścia między ekranami na iOS (iPhone 13, iOS 16.4, Flutter 3.10.0, Impeller włączony) zauważam sporadyczne janki. Problem występuje tylko z Impellerem. Po wyłączeniu Impellera animacja działa płynnie.

Przykład kodu:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Pierwszy ekran')),
      body: Center(
        child: ElevatedButton(
          child: Text('Przejdź do drugiego ekranu'),
          onPressed: () {
            Navigator.push(
              context,
              PageRouteBuilder(
                pageBuilder: (context, animation, secondaryAnimation) => SecondScreen(),
                transitionsBuilder: (context, animation, secondaryAnimation, child) {
                  const begin = Offset(1.0, 0.0);
                  const end = Offset.zero;
                  const curve = Curves.ease;

                  var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

                  return SlideTransition(
                    position: animation.drive(tween),
                    child: child,
                  );
                },
              ),
            );
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Drugi ekran')),
      body: Center(
        child: Text('To jest drugi ekran'),
      ),
    );
  }
}

Logi:

(Dołącz logi aplikacji z momentu wystąpienia problemu)

Informacje o środowisku:

  • Flutter: 3.10.0
  • System operacyjny: iOS 16.4
  • Urządzenie: iPhone 13
  • Impeller: Włączony

Przykłady kodu i implementacji

Oto kilka przykładów kodu, które pokazują, jak używać Impellera w praktyce (pamiętaj, że w większości przypadków nie musisz nic robić, Impeller powinien działać automatycznie!):

Przykład 1: Prosta animacja z użyciem AnimatedBuilder

Ten przykład pokazuje, jak stworzyć prostą animację z użyciem AnimatedBuilder. Impeller powinien renderować tę animację płynniej niż Skia.

import 'package:flutter/material.dart';

class AnimatedSquare extends StatefulWidget {
  @override
  _AnimatedSquareState createState() => _AnimatedSquareState();
}

class _AnimatedSquareState extends State<AnimatedSquare> with SingleTickerProviderStateMixin {
  AnimationController? _controller;
  Animation<double>? _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );
    _animation = Tween<double>(begin: 0, end: 2 * 3.14159).animate(_controller!)
      ..addListener(() {
        setState(() {});
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _controller!.repeat();
        } else if (status == AnimationStatus.dismissed) {
          _controller!.forward();
        }
      });
    _controller!.forward();
  }

  @override
  void dispose() {
    _controller!.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Transform.rotate(
      angle: _animation!.value,
      child: Container(
        width: 100,
        height: 100,
        color: Colors.blue,
      ),
    );
  }
}

Wyjaśnienie:

  • AnimatedSquare to StatefulWidget, który zawiera animację obracającego się kwadratu.
  • AnimationController kontroluje postęp animacji.
  • Tween definiuje zakres wartości animacji (od 0 do 2π radianów).
  • Transform.rotate obraca kwadrat o kąt określony przez wartość animacji.
  • setState(() {}) powoduje przebudowanie widgetu przy każdej zmianie wartości animacji.

Przykład 2: Użycie ShaderMask

Ten przykład pokazuje, jak użyć ShaderMask do stworzenia efektu maskowania. Impeller powinien renderować ten efekt wydajniej niż Skia.

import 'package:flutter/material.dart';

class ShaderMaskExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ShaderMask(
        shaderCallback: (Rect bounds) {
          return LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [Colors.black, Colors.transparent],
          ).createShader(bounds);
        },
        blendMode: BlendMode.dstIn,
        child: Image.network(
          'https://via.placeholder.com/300x200',
          width: 300,
          height: 200,
        ),
      ),
    );
  }
}

Wyjaśnienie:

  • ShaderMask pozwala na maskowanie widgetu za pomocą shadera.
  • shaderCallback definiuje shader, który będzie używany do maskowania. W tym przypadku używamy gradientu liniowego.
  • blendMode określa sposób łączenia shadera z widgetem. W tym przypadku używamy BlendMode.dstIn, który powoduje, że widoczne są tylko te obszary widgetu, które są pokryte przez shader.

Pamiętaj: Te przykłady mają na celu pokazanie, jak używać standardowych widgetów Fluttera. Impeller powinien automatycznie optymalizować rendering tych widgetów, bez konieczności wprowadzania jakichkolwiek zmian w kodzie.

Podsumowanie

Impeller to obiecująca inicjatywa, która ma potencjał, aby znacząco poprawić wydajność i jakość grafiki w aplikacjach Fluttera. Chociaż jest to stosunkowo nowy silnik renderujący, warto się nim zainteresować i eksperymentować z nim w swoich projektach.

Przydatne linki

Dalsza nauka

Zachęcam Cię do dalszego eksplorowania tematu Impellera. Przeczytaj oficjalną dokumentację Fluttera, śledź postępy prac na GitHubie i eksperymentuj z Impellerem w swoich projektach. Pamiętaj, że jest to wciąż rozwijający się projekt, więc bądź na bieżąco z najnowszymi informacjami. Powodzenia!

Mam nadzieję że ten artykuł był dla Ciebie pomocny. Zapraszam do czytania innych postów na naszym blogu!

Polecane artykuły