CustomPainter — egy absztrakt osztály a Flutter SDK-ból, amely teljes körű vezérlést biztosít a fejlesztőknek az egyedi 2D grafika Canvas-on történő megjelenítéséhez. A fejlesztő létrehozza a CustomPainter alosztályát és implementál két kötelező metódust: paint(Canvas, Size) és shouldRepaint(covariant CustomPainter oldDelegate). A Flutter API Documentation (2026) szerint a CustomPainter a CustomPaint widget-tel együtt használatos, amely a megjelenítést a átadott CustomPainter objektumra delegálja minden animációs képkockánál vagy állapotváltozásnál.
Főbb pontok
CustomPainter — a Flutter alaposztálya tetszőleges 2D grafika implementálásához. Felváltja az onDraw felülírásának megközelítését Android-ban, rugalmasabb és deklaratívabb megjelenítési modellt biztosítva. A fejlesztő létrehozza a CustomPainter alosztályát, felülírja a paint metódust, ahol megkapja a Canvas-t és Size-t, és végrehajtja az összes rajzolási műveletet a Canvas-on. A CustomPaint widget kezeli a paint meghívását és integrálja az eredményt a widget-fába.
A legfontosabb különbség a CustomPainter és a közvetlen Canvas-on keresztüli rajzolás között — a megjelenítési logika (paint) és az újrarajzolás triggerének (shouldRepaint) szétválasztása. A Flutter rendszer shouldRepaint-et hív minden alkalommal, amikor a szülő widget megváltozik. Ha a shouldRepaint false-t ad vissza, a paint nem kerül meghívásra, ami erőforrásokat takarít meg. Ez a mechanizmus analóg a shouldRebuild-del a RenderObject-ban, és része a Flutter architektúrájának a három fájával (Widget, Element, RenderObject).
CustomPaint — egy widget, amely a painter (háttérréteg) és foregroundPainter (előtérréteg) paramétereket fogadja. Mindkét paraméter CustomPainter objektumokat fogad. A háttér painter a CustomPaint gyermeke előtt rajzol, a foregroundPainter pedig utána. Ez lehetővé teszi a CustomPaint használatát dekoratív háttérrel vagy fedőréteggel rendelkező konténerként, anélkül hogy elveszítené a szokásos widgetek beágyazásának lehetőségét.
Canvas — a központi objektum a rajzoláshoz a Flutter-ben, amely a grafikus felületet inkapszulálja. Körülbelül 40 rajzolási metódust biztosít: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints és mások. A Flutter-ben a Canvas megfelel az Android Canvas analóg osztályának, de a Dart és a Flutter Engine architektúrájához igazították. A Canvas egy koordináta-rendszerben működik, ahol a kezdőpont (0, 0) a bal felső sarok.
Paint — nem összetévesztendő a paint() metódussal — egy osztály, amely meghatározza a rajzolási stílust: szín (color), vonalvastagság (strokeWidth), stílus (style: fill vagy stroke), átlátszóság, keverési mód (blendMode) és simító maszk. A Paint a rajzolás előtt van konfigurálva, és átadásra kerül a Canvas metódusainak. Fontos: A Paint egy változtatható objektum, de ajánlott egyszer létrehozni és újrahasználni, csak a szükséges paramétereket módosítva.
| Canvas metódus | Rendeltetés | Paraméterek |
|---|---|---|
| drawCircle | Kör rajzolása | Offset center, double radius, Paint paint |
| drawLine | Vonal rajzolása | Offset p1, Offset p2, Paint paint |
| drawRect | Téglalap rajzolása | Rect rect, Paint paint |
| drawPath | Tetszőleges kontúr rajzolása | Path path, Paint paint |
| drawArc | Ív vagy szektor rajzolása | Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint |
| drawRRect | Lekerekített sarkú téglalap | RRect rrect, Paint paint |
| drawOval | Ovális rajzolása | Rect rect, Paint paint |
| drawImage | Kép rajzolása Image-ből | Image image, Offset p, Paint paint |
Canvas támogatja a koordináta-rendszer transzformációit: translate(double dx, double dy) eltolja a koordináták kezdőpontját; rotate(double radians) forgatja a rendszert; scale(double sx, double sy) skáláz; skew(double sx, double sy) ferde. A transzformáció előtt ajánlott a Canvas állapotának mentése a save() segítségével, majd befejezés után visszaállítása a restore() segítségével. Ez megakadályozza a transzformációk felhalmozódását a paint későbbi meghívásaiban.
paint(Canvas canvas, Size size) — a kötelező metódus, amelyben a teljes rajzolási logika implementálva van. A canvas paraméter — a grafikus felület a rajzoláshoz. A size paraméter — a rajzoláshoz allokált terület mérete (szélesség és magasság logikai pixelekben). A Canvas előre úgy van konfigurálva, hogy a koordináta kezdőpont (0,0) megfeleljen a rajzolási terület bal felső sarkának. Minden koordináta ehhez a kezdőponthoz viszonyítva van meghatározva.
shouldRepaint(covariant CustomPainter oldDelegate) — a kötelező metódus, amely optimalizálja az újrarajzolás gyakoriságát. A Flutter shouldRepaint-et hív a widget-fa minden olyan frissítésekor, amely érinti a CustomPainter-t. Ha a metódus false-t ad vissza, a már megrajzolt kép érvényesnek tekintendő, és a paint nem kerül meghívásra. Tipikus implementáció: return oldDelegate.someField != someField — a régi és új painter mezőinek összehasonlítása az újrarajzolás szükségességének meghatározásához.
Animált vagy folyamatosan változó képek esetén (órák, töltésjelzők, grafikonok) a shouldRepaint-nek true-t kell visszaadnia minden állapotváltozásnál. Ha az objektum statikus (logó, ikon, háttér), a shouldRepaint false-t ad vissza az első megjelenítés után. A Flutter automatikusan gyorsítótárazza a paint eredményét, és csak a méret, átlátszóság változásakor vagy a shouldRepaint meghívásakor rajzol újra.
Hozzunk létre GaugePainter-t — egy egyedi kör alakú jelzőt animált kitöltéssel. A CustomPainter fogadja az aktuális értéket (progress 0.0-tól 1.0-ig), és egy ívet rajzol gradienst alkalmazva. A shouldRepaint metódusban a progress érték összehasonlítása történik az optimalizáció érdekében. A CustomPaint a widget-fába kerül a size paraméterrel a rajzolási terület korlátozásához.
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;
}
}
A példa egy hullámgrafikont rajzol Path segítségével. A Path lehetővé teszi tetszőleges kontúrok építését vonalakból, Bezier-görbékből és ívekből. Az építés után a kontúr kitöltésre kerül színnel vagy körvonalazásra vonallal. A példában a grafikon egy hullámformát imitáló pontok tömbje alapján épül fel.
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;
}
}
painter (háttér) a CustomPaint gyermeke előtt rajzol. Ha a CustomPaint-nek van gyermeke (child paraméter), a háttér painter alatta jelenik meg. Ez dekorációhoz alkalmas: folyamatjelző sávok, háttérminták, vízjelek. foregroundPainter a CustomPaint gyermeke után, azaz fölötte rajzol. Fedőrétegekhez használatos: tükröződések, maszkok, mérővonalzók, megjegyzések.
A különbség a painter és foregroundPainter között kritikus a Z-order szempontjából. Ha valamit a tartalom mögé kell rajzolni — használja a painter-t. Ha a tartalom fölé — foregroundPainter-t. Bizonyos esetekben mindkét painter használható egyszerre: háttér a painter-en keresztül, címke-fedőréteg a foregroundPainter-en keresztül. Ez lehetővé teszi a különböző grafikus rétegek logikájának szétválasztását két külön CustomPainter osztályba.
A size paraméter a paint metódusban mindkét painter esetében azonos, és a CustomPaint számára allokált terület mérete határozza meg. A méret explicit módon beállítható a size paraméteren keresztül a CustomPaint-ben, vagy implicit módon a szülő konténeren keresztül. Ha a size egyenlő Size.zero-val, a paint nem kerül meghívásra — a Flutter kihagyja a megjelenítést nulla terület esetén. Ezt a viselkedést figyelembe kell venni az elem animált megjelenésekor.
A fő szabály a CustomPainter teljesítményéhez — ne hozzon létre objektumokat a paint metóduson belül. A Canvas, Paint, Path, Rect, Offset és más rajzolási objektumokat a CustomPainter konstruktorában kell létrehozni vagy gyorsítótárazni. Az objektumok létrehozása a paint-en belül allokációkhoz vezet minden képkockánál, ami 60 képkocka/másodpercnél gyakori szemétgyűjtést és animációs akadozást (jank) okoz.
A Path gyorsítótárazása jelentősen felgyorsítja az ismétlődő összetett kontúrok megjelenítését. Ha a kontúr nem változik a képkockák között (pl. háttérrács), hozza létre a Path-t egyszer és használja többször. RepaintBoundary — egy burkoló widget, amely megakadályozza a CustomPainter újrarajzolását a felső widget-fa változásakor. Ha a CustomPainter statikus, csomagolja be RepaintBoundary-ba az elkülönítéshez.
Animált CustomPainter-ekhez gyakori újrarajzolással használjon Ticker-t vagy AnimationController-t, a shouldRepaint pedig adjon vissza true-t az animált érték változásakor. Optimális az animált mező összehasonlítása a shouldRepaint-ben, nem pedig minden képkockán szükségtelenül újrarajzolni. Rect gyorsítótárazás összetett gradiensekhez: ha a gradiens nem változtat színeket, a Shader gyorsítótárazható a Paint.shader-en keresztül.
Gyakran Ismételt Kérdések
CustomPainter — egy szerződés-osztály, amely meghatározza, mit és hogyan kell rajzolni a Canvas-on. Canvas — maga a rajzolási objektum, amely metódusokat biztosít (drawCircle, drawLine stb.). A CustomPainter a Canvas-t a paint metóduson keresztül kapja meg és kezeli annak használatát. Egy Canvas-t különböző CustomPainter-ek használhatnak különböző időpontokban.
Nem, a CustomPainter-nek nincs értelme CustomPaint nélkül. A CustomPaint az a widget, amely meghívja a CustomPainter paint metódusát és megjeleníti az eredményt a képernyőn. CustomPaint nélkül a Canvas nem jön létre, és a paint nem hajtódik végre. Lehetséges azonban saját RenderObject implementálása Canvas-szal, de ez egy alacsonyabb szintű megközelítés.
Használjon AnimationController-t Ticker-rel, adja át az animált értéket a CustomPainter-nek a konstruktor paraméterein keresztül. Az érték változásakor hívja meg a setState()-t a CustomPaint-et tartalmazó widget-ben, ami kiváltja a shouldRepaint-et. Ha a shouldRepaint true-t ad vissza, a rendszer meghívja a paint-et az animáció új képkockájához.
Az ok szinte mindig a shouldRepaint, amely false-t ad vissza. Ellenőrizze, hogy az oldDelegate mezői helyesen összehasonlításra kerülnek-e az új értékekkel. Ha változtatható objektumot használ, előfordulhat, hogy a shouldRepaint nem észleli a változásokat, mivel az objektumra való hivatkozás ugyanaz marad. Használjon ==-t vagy megváltoztathatatlan objektumokat a helyes összehasonlításhoz.
Igen, a CustomPainter egyedi dekorációk rajzolására használható összetett listákban. Azonban vegye figyelembe, hogy az elemek átrendezésekor a ListView-n belüli RepaintBoundary gyorsítótárazza az egyes elemek megjelenítését. A CustomPainter-nek könnyűnek kell lennie, és nem szabad nehéz műveleteket tartalmaznia a paint-ben, hogy ne lassítsa a húzási animációt.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is