CustomPainter — vad är det, metoderna paint och shouldRepaint i Flutter

Författare: IT Sectr Publicerad: 2026-07-22 Lästid: 10 min

CustomPainter — är en abstrakt klass från Flutter SDK som ger utvecklare fullständig kontroll över rendering av anpassad 2D-grafik på Canvas. Utvecklaren skapar en underklass till CustomPainter och implementerar två obligatoriska metoder: paint(Canvas, Size) och shouldRepaint(covariant CustomPainter oldDelegate). Enligt Flutter API Documentation (2026) används CustomPainter tillsammans med widgeten CustomPaint, som delegerar renderingen till det överlämnade CustomPainter-objektet vid varje bildruta av en animation eller tillståndsändring.

Huvudpunkter

  • CustomPainter — abstrakt Flutter-klass som definierar anpassad rendering på Canvas via metoderna paint och shouldRepaint
  • CustomPaint — widget som tar emot CustomPainter som parameter painter eller foregroundPainter och visar resultatet på skärmen
  • Canvas — objekt som tillhandahåller ritmetoder (drawLine, drawCircle, drawPath, drawRect, drawArc och andra)
  • shouldRepaint — optimeringsmetod som avgör om objektet måste ritas om vid uppdatering
  • size — storleken på ritområdet, som skickas till paint(Canvas canvas, Size size) från den överordnade widgeten

Vad är CustomPainter?

CustomPainter — är basklassen i Flutter för att implementera godtycklig 2D-grafik. Den ersätter metoden med att åsidosätta onDraw i Android och ger en mer flexibel och deklarativ renderingsmodell. Utvecklaren skapar en underklass till CustomPainter, åsidosätter metoden paint, där den får Canvas och Size, och utför alla ritoperationer på Canvas. Widgeten CustomPaint hanterar anropet till paint och integrerar resultatet i widget-trädet.

Den viktigaste skillnaden mellan CustomPainter och direkt ritning via Canvas — separationen av renderingslogik (paint) och utlösaren för omritning (shouldRepaint). Flutter-systemet anropar shouldRepaint varje gång den överordnade widgeten ändras. Om shouldRepaint returnerar false anropas inte paint, vilket sparar resurser. Denna mekanism är analog med shouldRebuild i RenderObject och är en del av Flutter-arkitekturen med dess tre träd (Widget, Element, RenderObject).

CustomPaint — är en widget som tar emot parametrarna painter (bakgrundslager) och foregroundPainter (förgrundslager). Båda parametrarna tar emot CustomPainter-objekt. Bakgrundsmålaren ritar före CustomPaint-barnet och foregroundPainter efter det. Detta gör det möjligt att använda CustomPaint som en behållare med dekorativ bakgrund eller överlagring, utan att förlora möjligheten att bädda in vanliga widgetar inuti.

Canvas och Paint i Flutter

Canvas — är det centrala objektet för ritning i Flutter, som kapslar in den grafiska ytan. Det tillhandahåller cirka 40 ritmetoder: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints och andra. Canvas i Flutter motsvarar den analoga klassen i Android Canvas, men är anpassad till arkitekturen i Dart och Flutter Engine. Canvas arbetar i ett koordinatsystem där origo (0, 0) är det övre vänstra hörnet.

Paint — inte att förväxla med metoden paint() — är en klass som definierar ritstilen: färg (color), linjetjocklek (strokeWidth), stil (style: fill eller stroke), genomskinlighet, blandningsläge (blendMode) och utjämningsmask. Paint konfigureras före ritning och skickas till Canvas-metoderna. Viktigt: Paint är ett föränderligt objekt, men det rekommenderas att skapa det en gång och återanvända, endast ändra de nödvändiga parametrarna.

Canvas-metodÄndamålParametrar
drawCircleRita cirkelOffset center, double radius, Paint paint
drawLineRita linjeOffset p1, Offset p2, Paint paint
drawRectRita rektangelRect rect, Paint paint
drawPathRita godtycklig konturPath path, Paint paint
drawArcRita båge eller sektorRect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint
drawRRectRektangel med rundade hörnRRect rrect, Paint paint
drawOvalRita ovalRect rect, Paint paint
drawImageRita bild från ImageImage image, Offset p, Paint paint

Canvas-transformeringar

Canvas stöder transformeringar av koordinatsystemet: translate(double dx, double dy) flyttar origo; rotate(double radians) roterar systemet; scale(double sx, double sy) skalar; skew(double sx, double sy) lutar. Före transformering rekommenderas att spara Canvas tillstånd via save() och efter slutförande återställa via restore(). Detta förhindrar ackumulering av transformeringar i efterföljande anrop av paint.

Metoderna paint och shouldRepaint

paint(Canvas canvas, Size size) — den obligatoriska metoden där all ritningslogik implementeras. Parametern canvas — den grafiska ytan för ritning. Parametern size — storleken på området som avsatts för ritning (bredd och höjd i logiska pixlar). Canvas är förkonfigurerad så att origo (0,0) motsvarar det övre vänstra hörnet av ritområdet. Alla koordinater anges i förhållande till detta origo.

shouldRepaint(covariant CustomPainter oldDelegate) — den obligatoriska metoden som optimerar frekvensen av omritning. Flutter anropar shouldRepaint vid varje uppdatering av widget-trädet som påverkar CustomPainter. Om metoden returnerar false anses den redan ritade bilden vara giltig och paint anropas inte. Typisk implementering: return oldDelegate.someField != someField — jämförelse av fälten hos den gamla och nya målaren för att avgöra behovet av omritning.

För animerade eller ständigt föränderliga bilder (klockor, laddningsindikatorer, grafer) ska shouldRepaint returnera true vid varje tillståndsändring. Om objektet är statiskt (logotyp, ikon, bakgrund) returnerar shouldRepaint false efter första renderingen. Flutter cachar automatiskt resultatet av paint och ritar om endast vid ändring av storlek, genomskinlighet eller anrop av shouldRepaint.

Exempel på anpassad ritning med CustomPainter i Dart

Låt oss skapa GaugePainter — en anpassad cirkulär indikator med animerad fyllning. CustomPainter tar emot det aktuella värdet (progress från 0.0 till 1.0) och ritar en båge med övertoning. I metoden shouldRepaint jämförs progress-värdet för optimering. CustomPaint placeras i widget-trädet med parametern size för att begränsa ritområdet.

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 med Path för komplexa former

Exemplet ritar en våggraf med hjälp av Path. Path gör det möjligt att bygga godtyckliga konturer från linjer, Bezier-kurvor och bågar. Efter bygget fylls konturen med färg eller kontureras med en linje. I exemplet byggs grafen baserat på en array av punkter som imiterar en vågform.

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 och backgroundPainter: skillnad

painter (bakgrund) ritar före CustomPaint-barnet. Om CustomPaint har ett barn (parametern child) visas bakgrundsmålaren under det. Detta är lämpligt för dekoration: förloppsindikatorer, bakgrundsmönster, vattenstämplar. foregroundPainter ritar efter CustomPaint-barnet, dvs ovanför det. Används för överlagringar: blänk, masker, mätlinjaler, anteckningar.

Skillnaden mellan painter och foregroundPainter är kritisk för Z-order. Om du behöver rita något bakom innehållet — använd painter. Om ovanför innehållet — foregroundPainter. I vissa fall kan du använda båda målarna samtidigt: bakgrund via painter, etikettöverlagring via foregroundPainter. Detta gör det möjligt att separera logiken för olika grafiska lager i två separata CustomPainter-klasser.

Parametern size i metoden paint är densamma för båda målarna och bestäms av storleken på området som avsatts för CustomPaint. Storleken kan ställas in explicit via size-parametern i CustomPaint eller implicit via den överordnade behållaren. Om size är lika med Size.zero anropas inte paint — Flutter hoppar över rendering för nollområdet. Detta beteende bör beaktas vid animerad visning av ett element.

Prestandaoptimering av CustomPainter

Huvudregeln för prestanda hos CustomPainter — skapa inte objekt inuti metoden paint. Canvas, Paint, Path, Rect, Offset och andra ritobjekt bör skapas i CustomPainters konstruktor eller cachas. Att skapa objekt inuti paint leder till allokeringar vid varje bildruta, vilket vid 60 bildrutor per sekund orsakar frekvent sophämtning och ryckig animation (jank).

Cachning av Path påskyndar avsevärt renderingen av repetitiva komplexa konturer. Om konturen inte ändras mellan bildrutorna (t.ex. bakgrundsrutnät), skapa Path en gång och använd den flera gånger. RepaintBoundary — en omslags-widget som förhindrar omritning av CustomPainter vid ändring av det överordnade widget-trädet. Om CustomPainter är statisk, linda in den i RepaintBoundary för isolering.

För animerade CustomPainter med frekvent omritning, använd Ticker eller AnimationController med shouldRepaint som returnerar true när det animerade värdet ändras. Optimalt är att jämföra det animerade fältet i shouldRepaint, inte att rita om vid varje bildruta i onödan. Rect-cachning för komplexa övertoningar: om övertoningen inte ändrar färger kan du cacha Shader via Paint.shader.

Vanliga frågor

Vad är skillnaden mellan CustomPainter och Canvas i Flutter?

CustomPainter — är en kontraktsklass som definierar vad och hur man ritar på Canvas. Canvas — själva ritobjektet som tillhandahåller metoder (drawCircle, drawLine etc.). CustomPainter får Canvas via metoden paint och hanterar dess användning. En Canvas kan användas av olika CustomPainter vid olika tidpunkter.

Kan CustomPainter användas utan CustomPaint?

Nej, CustomPainter är meningslöst utan CustomPaint. CustomPaint är widgeten som anropar CustomPainters paint-metod och visar resultatet på skärmen. Utan CustomPaint skapas inte Canvas och paint körs inte. Man kan dock implementera en egen RenderObject med Canvas, men det är en lägre nivås metod.

Hur animerar man CustomPainter?

Använd AnimationController med Ticker, skicka det animerade värdet till CustomPainter via konstruktorparametrar. När värdet ändras, anropa setState() i widgeten som innehåller CustomPaint, vilket utlöser shouldRepaint. Om shouldRepaint returnerar true anropar systemet paint för den nya animeringsbildrutan.

Varför uppdateras inte CustomPainter när data ändras?

Orsaken är nästan alltid shouldRepaint som returnerar false. Kontrollera om fälten i oldDelegate jämförs korrekt med de nya värdena. Om ett föränderligt objekt används kanske shouldRepaint inte upptäcker ändringarna eftersom referensen till objektet förblir densamma. Använd == eller oföränderliga objekt för korrekt jämförelse.

Kan CustomPainter användas för animering i ReorderableListView?

Ja, CustomPainter används för att rita anpassade dekorationer i komplexa listor. Tänk dock på att vid omordning av element cachas renderingen av varje element av RepaintBoundary inuti ListView. CustomPainter bör vara lätt och inte innehålla tunga operationer i paint för att inte sakta ner drag-animeringen.

Sammanfattning

  • CustomPainter — abstrakt Flutter-klass för anpassad 2D-rendering med metoderna paint och shouldRepaint
  • CustomPaint — widget som tar emot painter (bakgrund) och foregroundPainter (förgrund) för att visa grafik
  • Canvas — objekt med ritmetoder: drawLine, drawCircle, drawPath, drawRect, drawArc och andra
  • Paint — klass som definierar ritstil: färg, tjocklek, stil (fill/stroke), genomskinlighet och blandningsläge
  • shouldRepaint — optimeringsmetod som returnerar true endast när fält som påverkar renderingen ändras
  • Att skapa objekt inuti paint är förbjudet — alla Paint, Path, Rect måste cachas för 60 fps
  • RepaintBoundary — widget för att isolera CustomPainter från omritningar av det överordnade widget-trädet

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också