CustomPainter — co to je, metody paint a shouldRepaint ve Flutter

Autor: IT Sectr Publikováno: 2026-07-22 Doba čtení: 10 min

CustomPainter — je abstraktní třída z Flutter SDK, která poskytuje vývojářům plnou kontrolu nad vykreslováním vlastní 2D grafiky na Canvas. Vývojář vytvoří podtřídu CustomPainter a implementuje dvě povinné metody: paint(Canvas, Size) a shouldRepaint(covariant CustomPainter oldDelegate). Podle Flutter API Documentation (2026) se CustomPainter používá společně s widgetem CustomPaint, který deleguje vykreslování předanému objektu CustomPainter v každém snímku animace nebo změny stavu.

Hlavní body

  • CustomPainter — abstraktní třída Flutter, která definuje vlastní vykreslování na Canvas pomocí metod paint a shouldRepaint
  • CustomPaint — widget, který přijímá CustomPainter jako parametr painter nebo foregroundPainter a zobrazuje výsledek na obrazovce
  • Canvas — objekt poskytující metody kreslení (drawLine, drawCircle, drawPath, drawRect, drawArc a další)
  • shouldRepaint — optimalizační metoda, která určuje, zda je třeba objekt při aktualizaci překreslit
  • size — velikost oblasti kreslení, předávaná do paint(Canvas canvas, Size size) z nadřazeného widgetu

Co je CustomPainter?

CustomPainter — je základní třída Flutter pro implementaci libovolné 2D grafiky. Nahrazuje přístup s přepisováním onDraw v Android a poskytuje flexibilnější a deklarativnější model vykreslování. Vývojář vytvoří podtřídu CustomPainter, přepíše metodu paint, kde obdrží Canvas a Size, a provádí na Canvas všechny operace kreslení. Widget CustomPaint spravuje volání paint a integruje výsledek do stromu widgetů.

Klíčový rozdíl mezi CustomPainter a přímým kreslením přes Canvas — oddělení logiky vykreslování (paint) a spouštěče překreslení (shouldRepaint). Systém Flutter volá shouldRepaint pokaždé, když se nadřazený widget změní. Pokud shouldRepaint vrátí false, paint se nevolá, což šetří zdroje. Tento mechanismus je analogický shouldRebuild v RenderObject a je součástí architektury Flutter s jejími třemi stromy (Widget, Element, RenderObject).

CustomPaint — je widget, který přijímá parametry painter (vrstva pozadí) a foregroundPainter (popředí). Oba parametry přijímají objekty CustomPainter. Malíř pozadí kreslí před potomkem CustomPaint a foregroundPainter po něm. To umožňuje používat CustomPaint jako kontejner s dekorativním pozadím nebo překrytím, aniž by se ztratila možnost vkládání běžných widgetů dovnitř.

Canvas a Paint ve Flutter

Canvas — je centrální objekt pro kreslení ve Flutter, který zapouzdřuje grafický povrch. Poskytuje asi 40 metod kreslení: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints a další. Canvas ve Flutter odpovídá analogické třídě v Android Canvas, ale je přizpůsoben architektuře Dart a Flutter Engine. Canvas pracuje v souřadnicovém systému, kde počátek (0, 0) je levý horní roh.

Paint — nezaměňovat s metodou paint() — je třída, která definuje styl kreslení: barvu (color), tloušťku čáry (strokeWidth), styl (style: fill nebo stroke), průhlednost, režim míchání (blendMode) a masku pro vyhlazování. Paint se konfiguruje před kreslením a předává se metodám Canvas. Důležité: Paint je měnitelný objekt, ale doporučuje se vytvořit jej jednou a znovu používat, měnit pouze potřebné parametry.

Metoda CanvasÚčelParametry
drawCircleKreslení kruhuOffset center, double radius, Paint paint
drawLineKreslení úsečkyOffset p1, Offset p2, Paint paint
drawRectKreslení obdélníkuRect rect, Paint paint
drawPathKreslení libovolného obrysuPath path, Paint paint
drawArcKreslení oblouku nebo výsečeRect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint
drawRRectObdélník se zaoblenými rohyRRect rrect, Paint paint
drawOvalKreslení ováluRect rect, Paint paint
drawImageKreslení obrázku z ImageImage image, Offset p, Paint paint

Transformace Canvas

Canvas podporuje transformace souřadnicového systému: translate(double dx, double dy) posouvá počátek souřadnic; rotate(double radians) otáčí systém; scale(double sx, double sy) mění měřítko; skew(double sx, double sy) naklání. Před transformací se doporučuje uložit stav Canvas pomocí save() a po dokončení obnovit pomocí restore(). Tím se zabrání hromadění transformací v následujících voláních paint.

Metody paint a shouldRepaint

paint(Canvas canvas, Size size) — povinná metoda, ve které je implementována veškerá logika kreslení. Parametr canvas — grafický povrch pro kreslení. Parametr size — velikost oblasti určené pro kreslení (šířka a výška v logických pixelech). Canvas je předem nakonfigurován tak, že počátek souřadnic (0,0) odpovídá levému hornímu rohu oblasti kreslení. Všechny souřadnice jsou určeny vzhledem k tomuto počátku.

shouldRepaint(covariant CustomPainter oldDelegate) — povinná metoda, která optimalizuje frekvenci překreslování. Flutter volá shouldRepaint při každé aktualizaci stromu widgetů, která ovlivňuje CustomPainter. Pokud metoda vrátí false, již nakreslený obrázek je považován za platný a paint se nevolá. Typická implementace: return oldDelegate.someField != someField — porovnání polí starého a nového painteru pro určení potřeby překreslení.

Pro animované nebo neustále se měnící obrázky (hodiny, indikátory načítání, grafy) by shouldRepaint měl vracet true při každé změně stavu. Pokud je objekt statický (logo, ikona, pozadí), shouldRepaint vrací false po prvním vykreslení. Flutter automaticky ukládá do mezipaměti výsledek paint a překresluje pouze při změně velikosti, průhlednosti nebo volání shouldRepaint.

Příklad vlastního kreslení s CustomPainter v Dart

Vytvořme GaugePainter — vlastní kruhový indikátor s animovaným plněním. CustomPainter přijímá aktuální hodnotu (progress od 0.0 do 1.0) a kreslí oblouk s přechodem. V metodě shouldRepaint se porovnává hodnota progress pro optimalizaci. CustomPaint je umístěn do stromu widgetů s parametrem size pro omezení oblasti kreslení.

dart
class GaugePainter extends CustomPainter {
    final double progress;
    final Color startColor;
    final Color endColor;

    GaugePainter({
        required this.progress,
        this.startColor = Colors.blue,
        this.endColor = Colors.cyan,
    });

    @override
    void paint(Canvas canvas, Size size) {
        final center = Offset(size.width / 2,
            size.height / 2);
        final radius = center.dx - 12;
        final bgPaint = Paint()
            ..color = Colors.grey.withOpacity(0.3)
            ..style = PaintingStyle.stroke
            ..strokeWidth = 16
            ..strokeCap = StrokeCap.round;

        final progressPaint = Paint()
            ..shader = LinearGradient(
                colors: [startColor, endColor],
            ).createShader(
                Rect.fromCircle(
                    center, radius))
            ..style = PaintingStyle.stroke
            ..strokeWidth = 16
            ..strokeCap = StrokeCap.round;

        canvas.drawCircle(center, radius, bgPaint);
        canvas.drawArc(
            Rect.fromCircle(center, radius),
            -math.pi / 2,
            2 * math.pi * progress,
            false, progressPaint);
    }

    @override
    bool shouldRepaint(
        GaugePainter oldDelegate) {
        return oldDelegate.progress != progress;
    }
}

CustomPainter s Path pro složité tvary

Příklad kreslí vlnový graf pomocí Path. Path umožňuje vytvářet libovolné obrysy z čar, Bezierových křivek a oblouků. Po vytvoření je obrys vyplněn barvou nebo ohraničen čárou. V příkladu je graf vytvořen na základě pole bodů imitujících vlnový tvar.

dart
class WavePainter extends CustomPainter {
    final List<double> values;

    WavePainter({required this.values});

    @override
    void paint(Canvas canvas, Size size) {
        final path = Path();
        final dx = size.width /
            (values.length - 1);

        path.moveTo(0, size.height);
        for (var i = 0; i < values.length; i++) {
            final x = i * dx;
            final y = size.height *
                (1 - values[i]);
            path.lineTo(x, y);
        }
        path.lineTo(size.width, size.height);
        path.close();

        final fillPaint = Paint()
            ..color = Colors.blue.withOpacity(0.3)
            ..style = PaintingStyle.fill;

        canvas.drawPath(path, fillPaint);
    }

    @override
    bool shouldRepaint(
        WavePainter oldDelegate) {
        return oldDelegate.values != values;
    }
}

ForegroundPainter a backgroundPainter: rozdíl

painter (pozadí) kreslí před potomkem CustomPaint. Pokud má CustomPaint potomka (parametr child), malíř pozadí se zobrazí pod ním. To je vhodné pro dekoraci: indikátory průběhu, vzory pozadí, vodoznaky. foregroundPainter kreslí po potomkovi CustomPaint, tedy nad ním. Používá se pro překryvy: odlesky, masky, měřicí pravítka, anotace.

Rozdíl mezi painter a foregroundPainter je kritický pro Z-order. Pokud potřebujete nakreslit něco za obsahem — použijte painter. Pokud nad obsahem — foregroundPainter. V některých případech můžete použít oba painter současně: pozadí přes painter, popisek-překryv přes foregroundPainter. To umožňuje oddělit logiku různých grafických vrstev do dvou samostatných tříd CustomPainter.

Parametr size v metodě paint je stejný pro oba painter a je určen velikostí oblasti vyhrazené pro CustomPaint. Velikost lze nastavit explicitně pomocí parametru size v CustomPaint nebo implicitně přes nadřazený kontejner. Pokud je size roven Size.zero, paint se nevolá — Flutter přeskočí vykreslování pro nulovou oblast. Toto chování je třeba zohlednit při animovaném zobrazení prvku.

Optimalizace výkonu CustomPainter

Hlavní pravidlo výkonu CustomPainter — nevytvářejte objekty uvnitř metody paint. Canvas, Paint, Path, Rect, Offset a další objekty kreslení by měly být vytvořeny v konstruktoru CustomPainter nebo ukládány do mezipaměti. Vytváření objektů uvnitř paint vede k alokacím v každém snímku, což při 60 snímcích za sekundu způsobuje časté sběry paměti a škubání animace (jank).

Ukládání Path do mezipaměti výrazně urychluje vykreslování opakujících se složitých obrysů. Pokud se obrys nemění mezi snímky (např. síť pozadí), vytvořte Path jednou a používejte jej opakovaně. RepaintBoundary — obalový widget, který zabraňuje překreslení CustomPainter při změně nadřazeného stromu widgetů. Pokud je CustomPainter statický, zabalte jej do RepaintBoundary pro izolaci.

Pro animované CustomPainter s častým překreslováním používejte Ticker nebo AnimationController s shouldRepaint vracejícím true při změně animované hodnoty. Optimální je porovnávat animované pole v shouldRepaint, nikoli překreslovat v každém snímku bez potřeby. Ukládání Rect do mezipaměti pro složité přechody: pokud přechod nemění barvy, lze uložit Shader do mezipaměti pomocí Paint.shader.

Často kladené otázky

Jaký je rozdíl mezi CustomPainter a Canvas ve Flutter?

CustomPainter — je třída-kontrakt, která určuje, co a jak kreslit na Canvas. Canvas — samotný objekt kreslení, který poskytuje metody (drawCircle, drawLine atd.). CustomPainter získává Canvas prostřednictvím metody paint a řídí jeho používání. Jeden Canvas může být používán různými CustomPainter v různých časech.

Lze použít CustomPainter bez CustomPaint?

Ne, CustomPainter nemá smysl bez CustomPaint. CustomPaint je widget, který volá metodu paint CustomPainter a zobrazuje výsledek na obrazovce. Bez CustomPaint nebude Canvas vytvořen a paint se neprovede. Lze však implementovat vlastní RenderObject s Canvas, ale to je přístup nižší úrovně.

Jak animovat CustomPainter?

Použijte AnimationController s Ticker, předávejte animovanou hodnotu do CustomPainter přes parametry konstruktoru. Při změně hodnoty zavolejte setState() ve widgetu obsahujícím CustomPaint, což spustí shouldRepaint. Pokud shouldRepaint vrátí true, systém zavolá paint pro nový snímek animace.

Proč se CustomPainter neaktualizuje při změně dat?

Příčinou je téměř vždy shouldRepaint vracející false. Zkontrolujte, zda jsou pole oldDelegate správně porovnána s novými hodnotami. Pokud je použit měnitelný objekt, shouldRepaint nemusí zaznamenat změny, protože reference na objekt zůstává stejná. Pro správné porovnání použijte == nebo neměnné objekty.

Lze použít CustomPainter pro animaci v ReorderableListView?

Ano, CustomPainter se používá pro kreslení vlastních dekorací ve složitých seznamech. Mějte však na paměti, že při přeuspořádávání prvků RepaintBoundary uvnitř ListView ukládá do mezipaměti vykreslení každého prvku. CustomPainter by měl být lehký a neobsahovat těžké operace v paint, aby nezpomaloval animaci přetahování.

Shrnutí

  • CustomPainter — abstraktní třída Flutter pro vlastní 2D vykreslování s metodami paint a shouldRepaint
  • CustomPaint — widget přijímající painter (pozadí) a foregroundPainter (popředí) pro zobrazení grafiky
  • Canvas — objekt s metodami kreslení: drawLine, drawCircle, drawPath, drawRect, drawArc a dalšími
  • Paint — třída definující styl kreslení: barvu, tloušťku, styl (fill/stroke), průhlednost a režim míchání
  • shouldRepaint — optimalizační metoda vracející true pouze při změně polí ovlivňujících vykreslování
  • Vytváření objektů uvnitř paint je zakázáno — všechny Paint, Path, Rect musí být ukládány do mezipaměti pro 60 fps
  • RepaintBoundary — widget pro izolaci CustomPainter od překreslování nadřazeného stromu widgetů

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také