CustomPainter, Flutter SDK'dan soyut bir sınıftır ve geliştiricilere Canvas üzerinde özel 2D grafikleri işleme konusunda tam kontrol sağlar. Geliştirici, CustomPainter'ın bir alt sınıfını oluşturur ve iki zorunlu metodu uygular: paint(Canvas, Size) ve shouldRepaint(covariant CustomPainter oldDelegate). Flutter API Dokümantasyonu'na (2026) göre CustomPainter, her animasyon karesinde veya durum değişikliğinde işlemeyi sağlanan CustomPainter nesnesine devreden CustomPaint widget'ı ile birlikte kullanılır.
Ana Noktalar
CustomPainter, özel 2D grafikler uygulamak için Flutter'ın temel sınıfıdır. Android'de onDraw'ı geçersiz kılma yaklaşımının yerine daha esnek ve bildirimsel bir işleme modeli sağlar. Geliştirici, CustomPainter'ın bir alt sınıfını oluşturur, Canvas ve Size aldığı paint metodunu geçersiz kılar ve Canvas üzerinde tüm çizim işlemlerini gerçekleştirir. CustomPaint widget'ı paint çağrısını yönetir ve sonucu widget ağacına entegre eder.
CustomPainter ile Canvas üzerinden doğrudan çizim arasındaki temel fark, işleme mantığının (paint) ve yeniden çizim tetikleyicisinin (shouldRepaint) ayrılmasıdır. Flutter sistemi, üst widget her değiştiğinde shouldRepaint'i çağırır. shouldRepaint false döndürürse, paint çağrılmaz ve kaynaklar korunur. Bu mekanizma, RenderObject'teki shouldRebuild'e benzer ve üç ağacı (Widget, Element, RenderObject) ile Flutter mimarisinin bir parçasıdır.
CustomPaint, painter (arka plan katmanı) ve foregroundPainter (ön plan katmanı) parametrelerini kabul eden bir widget'tır. Her iki parametre de CustomPainter nesnelerini kabul eder. Arka plan painter'ı, CustomPaint'in alt widget'ından önce çizilir ve foregroundPainter sonra çizilir. Bu, CustomPaint'in içine normal widget'lar yerleştirme yeteneğini kaybetmeden dekoratif arka plan veya kaplamalı bir kapsayıcı olarak kullanılmasını sağlar.
Canvas Flutter'da merkezi çizim nesnesidir ve grafik yüzeyini kapsüller. Yaklaşık 40 çizim metodu sağlar: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints ve diğerleri. Flutter'daki Canvas, Android Canvas'daki benzer bir sınıfa karşılık gelir ancak Dart mimarisi ve Flutter Engine'e uyarlanmıştır. Canvas, orijin (0, 0) sol üst köşe olan bir koordinat sisteminde çalışır.
Paint — paint() metodu ile karıştırılmamalıdır — çizim stilini tanımlayan bir sınıftır: renk (color), çizgi kalınlığı (strokeWidth), stil (style: fill veya stroke), opaklık, karışım modu (blendMode) ve kenar yumuşatma maskesi. Paint çizimden önce yapılandırılır ve Canvas metotlarına iletilir. Önemli: Paint değiştirilebilir bir nesnedir, ancak bir kez oluşturulup yalnızca gerekli parametreler değiştirilerek yeniden kullanılması önerilir.
| Canvas Metodu | Amaç | Parametreler |
|---|---|---|
| drawCircle | Daire çizer | Offset center, double radius, Paint paint |
| drawLine | Çizgi parçası çizer | Offset p1, Offset p2, Paint paint |
| drawRect | Dikdörtgen çizer | Rect rect, Paint paint |
| drawPath | İsteğe bağlı yol çizer | Path path, Paint paint |
| drawArc | Yay veya dilim çizer | Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint |
| drawRRect | Yuvarlak köşeli dikdörtgen | RRect rrect, Paint paint |
| drawOval | Oval çizer | Rect rect, Paint paint |
| drawImage | Image'dan resim çizer | Image image, Offset p, Paint paint |
Canvas koordinat sistemi dönüşümlerini destekler: translate(double dx, double dy) orijini kaydırır; rotate(double radians) sistemi döndürür; scale(double sx, double sy) ölçeklendirir; skew(double sx, double sy) eğer. Dönüşümden önce Canvas durumunun save() ile kaydedilmesi ve tamamlandıktan sonra restore() ile geri yüklenmesi önerilir. Bu, sonraki paint çağrılarında dönüşümlerin birikmesini önler.
paint(Canvas canvas, Size size) tüm çizim mantığını uygulayan zorunlu metottur. canvas parametresi çizim için grafik yüzeyidir. size parametresi çizim için ayrılan alanın boyutudur (mantıksal piksel cinsinden genişlik ve yükseklik). Canvas, orijin (0,0) çizim alanının sol üst köşesine karşılık gelecek şekilde önceden yapılandırılmıştır. Tüm koordinatlar bu orijine göre belirtilir.
shouldRepaint(covariant CustomPainter oldDelegate) yeniden çizim sıklığını optimize eden zorunlu bir metottur. Flutter, CustomPainter'ı etkileyen her widget ağacı güncellemesinde shouldRepaint'i çağırır. Metot false döndürürse, önceden işlenmiş görüntü geçerli kabul edilir ve paint çağrılmaz. Tipik bir uygulama: return oldDelegate.someField != someField — yeniden çizim gereksinimini belirlemek için eski ve yeni painter alanlarının karşılaştırılması.
Animasyonlu veya sürekli değişen görüntüler (saatler, yükleme göstergeleri, grafikler) için shouldRepaint her durum değişikliğinde true döndürmelidir. Nesne statikse (logo, simge, arka plan), shouldRepaint ilk işlemeden sonra false döndürür. Flutter otomatik olarak paint sonucunu önbelleğe alır ve yalnızca boyut, opaklık veya shouldRepaint çağrısı değiştiğinde yeniden çizer.
GaugePainter oluşturalım — animasyonlu dolgulu özel bir dairesel gösterge. CustomPainter mevcut değeri (0.0 ile 1.0 arasında ilerleme) kabul eder ve gradyanlı bir yay çizer. shouldRepaint metodu optimizasyon için ilerleme değerini karşılaştırır. CustomPaint, çizim alanını sınırlamak için size parametresiyle widget ağacına yerleştirilir.
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;
}
}
Örnek, Path kullanarak bir dalga grafiği çizer. Path, çizgiler, Bezier eğrileri ve yaylardan isteğe bağlı konturlar oluşturmaya izin verir. Kontur oluşturulduktan sonra renkle doldurulur veya bir çizgiyle ana hatları çizilir. Örnekte, grafik bir dalga formunu simüle eden bir dizi noktadan oluşturulur.
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 (arka plan) CustomPaint'in alt widget'ından önce çizilir. CustomPaint'in bir alt widget'ı (child parametresi) varsa, arka plan painter'ı onun altında görüntülenir. Bu, dekorasyonlar için uygundur: ilerleme çubukları, arka plan desenleri, filigranlar. foregroundPainter alt widget'tan sonra, yani onun üzerinde çizilir. Kaplamalar için kullanılır: vurgular, maskeler, ölçüm cetvelleri, açıklamalar.
painter ve foregroundPainter arasındaki fark Z-sırası için kritiktir. İçeriğin arkasına bir şey çizmeniz gerekiyorsa — painter kullanın. İçeriğin üzerine ise — foregroundPainter kullanın. Bazı durumlarda, her iki painter'ı aynı anda kullanabilirsiniz: painter aracılığıyla arka plan, foregroundPainter aracılığıyla kaplama etiketi. Bu, farklı grafik katmanlarının mantığını iki ayrı CustomPainter sınıfına ayırmaya olanak tanır.
Paint metodundaki size parametresi her iki painter için de aynıdır ve CustomPaint için ayrılan alanın boyutuna göre belirlenir. Boyut, CustomPaint'teki size parametresiyle açıkça veya üst kapsayıcı aracılığıyla örtük olarak ayarlanabilir. Boyut Size.zero ise, paint çağrılmaz — Flutter sıfır boyutlu bir alan için işlemeyi atlar. Bu davranış, öğe görünümü canlandırılırken dikkate alınmalıdır.
CustomPainter performansının ana kuralı, paint metodu içinde nesne oluşturmamaktır. Canvas, Paint, Path, Rect, Offset ve diğer çizim nesneleri CustomPainter yapıcısında oluşturulmalı veya önbelleğe alınmalıdır. Paint içinde nesne oluşturmak, her karede bellek tahsisine yol açar ve saniyede 60 karede sık sık çöp toplamaya ve animasyon takılmalarına (jank) neden olur.
Path önbelleğe alma, tekrarlanan karmaşık yolların işlenmesini önemli ölçüde hızlandırır. Yol kareler arasında değişmiyorsa (örneğin, bir arka plan ızgarası), Path'i bir kez oluşturun ve birden çok kez yeniden kullanın. RepaintBoundary, üst widget ağacı değiştiğinde CustomPainter'ın yeniden çizilmesini önleyen bir sarmalayıcı widget'tır. CustomPainter statikse, yalıtım için onu bir RepaintBoundary ile sarın.
Sık yeniden çizim içeren animasyonlu CustomPainter için, animasyon değeri her değiştiğinde true döndüren shouldRepaint ile Ticker veya AnimationController kullanın. Gereksiz yere her karede yeniden çizmek yerine shouldRepaint'te animasyon alanını karşılaştırmak en uygunudur. Karmaşık gradyanlar için Rect önbelleğe alma: gradyan renkleri değişmiyorsa, Paint.shader aracılığıyla Shader'ı önbelleğe alabilirsiniz.
Sıkça Sorulan Sorular
CustomPainter, Canvas üzerinde neyin ve nasıl çizileceğini tanımlayan bir sözleşme sınıfıdır. Canvas, metotlar (drawCircle, drawLine vb.) sağlayan çizim nesnesinin kendisidir. CustomPainter, paint metodu aracılığıyla Canvas'ı alır ve kullanımını kontrol eder. Aynı Canvas, farklı zamanlarda farklı CustomPainter'lar tarafından kullanılabilir.
Hayır, CustomPainter'ın CustomPaint olmadan bir anlamı yoktur. CustomPaint, CustomPainter'ın paint metodunu çağıran ve sonucu ekranda görüntüleyen widget'tır. CustomPaint olmadan Canvas oluşturulmaz ve paint yürütülmez. Ancak, Canvas ile kendi RenderObject'unuzu uygulayabilirsiniz, ancak bu daha düşük seviyeli bir yaklaşımdır.
Yapıcı parametreleri aracılığıyla animasyon değerini CustomPainter'a geçirerek bir Ticker ile AnimationController kullanın. Değer değiştiğinde, CustomPaint'i içeren widget'ta setState() çağrısı yapın, bu shouldRepaint çağrısını tetikler. shouldRepaint true döndürürse, sistem yeni animasyon karesi için paint'i çağırır.
Nedeni neredeyse her zaman shouldRepaint'in false döndürmesidir. oldDelegate alanlarının yeni değerlerle doğru şekilde karşılaştırılıp karşılaştırılmadığını kontrol edin. Değiştirilebilir bir nesne kullanılıyorsa, nesne referansı aynı kaldığı için shouldRepaint değişiklikleri algılamayabilir. Doğru karşılaştırma için == veya değişmez nesneler kullanın.
Evet, CustomPainter karmaşık listelerde özel dekorasyonlar işlemek için kullanılır. Ancak, öğeleri yeniden sıralarken ListView içindeki RepaintBoundary'in her öğenin işlenmesini önbelleğe aldığını unutmayın. CustomPainter hafif olmalı ve sürükleme animasyonunu yavaşlatmamak için paint'te ağır işlemler içermemelidir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun