CustomPainter — ce este, metodele paint și shouldRepaint în Flutter

Autor: IT Sectr Publicat: 2026-07-22 Timp de citire: 10 min

CustomPainter — este o clasă abstractă din Flutter SDK, care oferă dezvoltatorilor control complet asupra redării de grafică 2D personalizată pe Canvas. Dezvoltatorul creează o subclasă CustomPainter și implementează două metode obligatorii: paint(Canvas, Size) și shouldRepaint(covariant CustomPainter oldDelegate). Conform Flutter API Documentation (2026), CustomPainter este utilizat împreună cu widget-ul CustomPaint, care delegă redarea obiectului CustomPainter primit la fiecare cadru de animație sau schimbare de stare.

Principalele puncte

  • CustomPainter — clasă abstractă Flutter care definește redarea personalizată pe Canvas prin metodele paint și shouldRepaint
  • CustomPaint — widget care primește CustomPainter ca parametru painter sau foregroundPainter și afișează rezultatul pe ecran
  • Canvas — obiect ce oferă metode de desenare (drawLine, drawCircle, drawPath, drawRect, drawArc și altele)
  • shouldRepaint — metodă de optimizare care determină dacă obiectul trebuie redesenat la actualizare
  • size — dimensiunea zonei de desenare, transmisă în paint(Canvas canvas, Size size) din widget-ul părinte

Ce este CustomPainter?

CustomPainter — este clasa de bază Flutter pentru implementarea graficii 2D arbitrare. Înlocuiește abordarea cu suprascrierea onDraw în Android, oferind un model de redare mai flexibil și declarativ. Dezvoltatorul creează o subclasă CustomPainter, suprascrie metoda paint, unde primește Canvas și Size, și execută toate operațiile de desenare pe Canvas. Widget-ul CustomPaint gestionează apelul paint și integrează rezultatul în arborele de widget-uri.

Diferența cheie dintre CustomPainter și desenarea directă prin Canvas — separarea logicii de redare (paint) și a declanșatorului de redesenare (shouldRepaint). Sistemul Flutter apelează shouldRepaint de fiecare dată când widget-ul părinte se schimbă. Dacă shouldRepaint returnează false, paint nu este apelat, economisind resurse. Acest mecanism este analog cu shouldRebuild în RenderObject și face parte din arhitectura Flutter cu cele trei arbori ale sale (Widget, Element, RenderObject).

CustomPaint — este un widget care primește parametrii painter (stratul de fundal) și foregroundPainter (stratul din față). Ambii parametri primesc obiecte CustomPainter. Painterul de fundal desenează înaintea copilului CustomPaint, iar foregroundPainter — după. Aceasta permite utilizarea CustomPaint ca un container cu fundal decorativ sau suprapunere, fără a pierde posibilitatea de a încorpora widget-uri obișnuite în interior.

Canvas și Paint în Flutter

Canvas — este obiectul central pentru desenare în Flutter, care încapsulează suprafața grafică. Oferă aproximativ 40 de metode de desenare: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints și altele. Canvas în Flutter corespunde clasei similare din Android Canvas, dar este adaptat arhitecturii Dart și Flutter Engine. Canvas funcționează într-un sistem de coordonate unde originea (0, 0) este colțul stânga sus.

Paint — a nu se confunda cu metoda paint() — este o clasă care definește stilul de desenare: culoare (color), grosimea liniei (strokeWidth), stil (style: fill sau stroke), transparență, modul de amestecare (blendMode) și masca de netezire. Paint se configurează înainte de desenare și se transmite metodelor Canvas. Important: Paint este un obiect mutabil, dar se recomandă crearea lui o singură dată și reutilizarea, modificând doar parametrii necesari.

Metoda CanvasDestinațieParametri
drawCircleDesenarea cerculuiOffset center, double radius, Paint paint
drawLineDesenarea segmentuluiOffset p1, Offset p2, Paint paint
drawRectDesenarea dreptunghiuluiRect rect, Paint paint
drawPathDesenarea conturului arbitrarPath path, Paint paint
drawArcDesenarea arcului sau sectoruluiRect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint
drawRRectDreptunghi cu colțuri rotunjiteRRect rrect, Paint paint
drawOvalDesenarea ovaluluiRect rect, Paint paint
drawImageDesenarea imaginii din ImageImage image, Offset p, Paint paint

Transformări Canvas

Canvas suportă transformări ale sistemului de coordonate: translate(double dx, double dy) deplasează originea coordonatelor; rotate(double radians) rotește sistemul; scale(double sx, double sy) scalează; skew(double sx, double sy) înclină. Înainte de transformare se recomandă salvarea stării Canvas prin save(), iar după finalizare restaurarea prin restore(). Aceasta previne acumularea transformărilor în apelurile ulterioare ale paint.

Metodele paint și shouldRepaint

paint(Canvas canvas, Size size) — metoda obligatorie în care se implementează întreaga logică de desenare. Parametrul canvas — suprafața grafică pentru desenare. Parametrul size — dimensiunea zonei alocate pentru desenare (lățime și înălțime în pixeli logici). Canvas este configurat astfel încât originea coordonatelor (0,0) corespunde colțului stânga sus al zonei de desenare. Toate coordonatele se stabilesc relativ la această origine.

shouldRepaint(covariant CustomPainter oldDelegate) — metoda obligatorie care optimizează frecvența redesenerii. Flutter apelează shouldRepaint la fiecare actualizare a arborelui de widget-uri care afectează CustomPainter. Dacă metoda returnează false, imaginea deja desenată este considerată validă și paint nu este apelat. Implementarea tipică: return oldDelegate.someField != someField — compararea câmpurilor painterului vechi și nou pentru determinarea necesității de redesenerare.

Pentru imagini animate sau în continuă schimbare (ceasuri, indicatoare de progres, grafice) shouldRepaint trebuie să returneze true la fiecare schimbare de stare. Dacă obiectul este static (logo, pictogramă, fundal), shouldRepaint returnează false după prima redare. Flutter stochează automat în cache rezultatul paint și redesenează doar la schimbarea dimensiunii, transparenței sau apelarea shouldRepaint.

Exemplu de desenare personalizată cu CustomPainter în Dart

Să creăm GaugePainter — un indicator circular personalizat cu umplere animată. CustomPainter primește valoarea curentă (progress de la 0.0 la 1.0) și desenează un arc cu gradient. În metoda shouldRepaint se compară valoarea progress pentru optimizare. CustomPaint este plasat în arborele de widget-uri cu parametrul size pentru limitarea zonei de desenare.

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 cu Path pentru figuri complexe

Exemplul desenează un grafic ondulat folosind Path. Path permite construirea de contururi arbitrare din linii, curbe Bezier și arcuri. După construire, conturul este umplut cu culoare sau conturat cu o linie. În exemplu, graficul este construit pe baza unui tablou de puncte care imită o formă de undă.

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 și backgroundPainter: diferența

painter (de fundal) desenează înaintea copilului CustomPaint. Dacă CustomPaint are un copil (parametrul child), painterul de fundal este afișat sub acesta. Acest lucru este potrivit pentru decorațiuni: bare de progres, modele de fundal, filigrane. foregroundPainter desenează după copilul CustomPaint, adică deasupra acestuia. Este folosit pentru suprapuneri: reflexii, măști, rigle de măsurare, adnotări.

Diferența dintre painter și foregroundPainter este critică pentru Z-order. Dacă trebuie să desenați ceva în spatele conținutului — folosiți painter. Dacă deasupra conținutului — foregroundPainter. În unele cazuri, puteți folosi ambii painter simultan: fundal prin painter, etichetă-suprapunere prin foregroundPainter. Aceasta permite separarea logicii diferitelor straturi grafice în două clase separate CustomPainter.

Parametrul size în metoda paint este același pentru ambii painter și este determinat de dimensiunea zonei alocate pentru CustomPaint. Dimensiunea poate fi setată explicit prin parametrul size în CustomPaint sau implicit prin containerul părinte. Dacă size este egal cu Size.zero, paint nu este apelat — Flutter omite redarea pentru zona zero. Acest comportament trebuie luat în considerare la apariția animată a elementului.

Optimizarea performanței CustomPainter

Regula principală a performanței CustomPainter — să nu creați obiecte în interiorul metodei paint. Canvas, Paint, Path, Rect, Offset și alte obiecte de desenare trebuie create în constructorul CustomPainter sau stocate în cache. Crearea obiectelor în interiorul paint duce la alocări la fiecare cadru, ceea ce la 60 de cadre pe secundă provoacă colectări frecvente de gunoi și sacadări ale animației (jank).

Stocarea în cache a Path accelerează semnificativ redarea contururilor complexe repetitive. Dacă conturul nu se schimbă între cadre (de exemplu, o grilă de fundal), creați Path o dată și folosiți-l de mai multe ori. RepaintBoundary — widget-ul-ambalaj care previne redesenerarea CustomPainter la schimbarea arborelui de widget-uri superior. Dacă CustomPainter este static, înfășurați-l în RepaintBoundary pentru izolare.

Pentru CustomPainter animate cu redesenerare frecventă folosiți Ticker sau AnimationController cu shouldRepaint care returnează true la schimbarea valorii animate. Optimal este să comparați câmpul animat în shouldRepaint, nu să redesenați la fiecare cadru fără necesitate. Stocarea în cache a Rect pentru gradiente complexe: dacă gradientul nu schimbă culorile, puteți stoca în cache Shader prin Paint.shader.

Întrebări frecvente

Care este diferența dintre CustomPainter și Canvas în Flutter?

CustomPainter — este o clasă-contract care definește ce și cum să deseneze pe Canvas. Canvas — obiectul de desenare în sine, care oferă metode (drawCircle, drawLine etc.). CustomPainter primește Canvas prin metoda paint și gestionează utilizarea acestuia. Un Canvas poate fi folosit de diferiți CustomPainter în momente diferite.

Se poate folosi CustomPainter fără CustomPaint?

Nu, CustomPainter nu are sens fără CustomPaint. CustomPaint este widget-ul care apelează metoda paint a CustomPainter și afișează rezultatul pe ecran. Fără CustomPaint, Canvas nu va fi creat și paint nu se va executa. Se poate, totuși, implementa propriul RenderObject cu Canvas, dar aceasta este o abordare de nivel inferior.

Cum să animez CustomPainter?

Folosiți AnimationController cu Ticker, transmițând valoarea animată în CustomPainter prin parametrii constructorului. La schimbarea valorii, apelați setState() în widget-ul care conține CustomPaint, ceea ce declanșează shouldRepaint. Dacă shouldRepaint returnează true, sistemul apelează paint pentru noul cadru de animație.

De ce CustomPainter nu se actualizează la schimbarea datelor?

Cauza este aproape întotdeauna shouldRepaint care returnează false. Verificați dacă câmpurile oldDelegate sunt comparate corect cu noile valori. Dacă se folosește un obiect mutabil, shouldRepaint poate să nu observe modificările, deoarece referința la obiect rămâne aceeași. Folosiți == sau obiecte imutabile pentru o comparație corectă.

Pot folosi CustomPainter pentru animație în ReorderableListView?

Da, CustomPainter este folosit pentru desenarea decorațiunilor personalizate în liste complexe. Totuși, rețineți că la reordonarea elementelor, RepaintBoundary din interiorul ListView stochează în cache redarea fiecărui element. CustomPainter trebuie să fie ușor și să nu conțină operații grele în paint pentru a nu încetini animația de tragere.

Rezumat

  • CustomPainter — clasă abstractă Flutter pentru redarea 2D personalizată cu metodele paint și shouldRepaint
  • CustomPaint — widget care primește painter (fundal) și foregroundPainter (prim-plan) pentru afișarea graficii
  • Canvas — obiect cu metode de desenare: drawLine, drawCircle, drawPath, drawRect, drawArc și altele
  • Paint — clasă care definește stilul de desenare: culoare, grosime, stil (fill/stroke), transparență și modul de amestecare
  • shouldRepaint — metodă de optimizare care returnează true doar la schimbarea câmpurilor care afectează redarea
  • Crearea obiectelor în interiorul paint este interzisă — toate Paint, Path, Rect trebuie stocate în cache pentru 60 fps
  • RepaintBoundary — widget pentru izolarea CustomPainter de redesenerările arborelui de widget-uri superior

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și