CustomPainter Flutter SDK کا ایک تجریدی کلاس ہے جو ڈویلپرز کو Canvas پر کسٹم 2D گرافکس رینڈر کرنے کا مکمل کنٹرول فراہم کرتا ہے۔ ڈویلپر CustomPainter کا ذیلی کلاس بناتا ہے اور دو لازمی طریقے لاگو کرتا ہے: paint(Canvas, Size) اور shouldRepaint(covariant CustomPainter oldDelegate)۔ Flutter API دستاویزات (2026) کے مطابق، CustomPainter CustomPaint ویجیٹ کے ساتھ مل کر استعمال ہوتا ہے، جو ہر اینیمیشن فریم یا اسٹیٹ تبدیلی پر فراہم کردہ CustomPainter آبجیکٹ کو رینڈرنگ سونپتا ہے۔
اہم نکات
CustomPainter کسٹم 2D گرافکس لاگو کرنے کے لیے Flutter کا بنیادی کلاس ہے۔ یہ Android میں onDraw کو اوور رائڈ کرنے کے طریقے کی جگہ لیتا ہے، جو زیادہ لچکدار اور اعلانیہ رینڈرنگ ماڈل فراہم کرتا ہے۔ ڈویلپر CustomPainter کا ذیلی کلاس بناتا ہے، paint طریقہ کو اوور رائڈ کرتا ہے جہاں وہ Canvas اور Size حاصل کرتا ہے، اور Canvas پر تمام ڈرائنگ آپریشنز انجام دیتا ہے۔ CustomPaint ویجیٹ paint کال کا انتظام کرتا ہے اور نتیجہ کو ویجیٹ ٹری میں ضم کرتا ہے۔
CustomPainter اور Canvas کے ذریعے براہ راست ڈرائنگ کے درمیان کلیدی فرق رینڈرنگ منطق (paint) اور دوبارہ پینٹ ٹریگر (shouldRepaint) کی علیحدگی ہے۔ Flutter سسٹم ہر بار جب پیرنٹ ویجیٹ تبدیل ہوتا ہے shouldRepaint کو کال کرتا ہے۔ اگر shouldRepaint false لوٹاتا ہے تو paint کال نہیں ہوتی، جس سے وسائل بچتے ہیں۔ یہ میکانزم RenderObject میں shouldRebuild جیسا ہے اور اپنے تین درختوں (Widget، Element، RenderObject) کے ساتھ Flutter آرکیٹیکچر کا حصہ ہے۔
CustomPaint ایک ویجیٹ ہے جو painter (پس منظر کی تہہ) اور foregroundPainter (پیش منظر کی تہہ) پیرامیٹرز قبول کرتا ہے۔ دونوں پیرامیٹرز CustomPainter آبجیکٹ قبول کرتے ہیں۔ پس منظر painter CustomPaint کے چائلڈ ویجیٹ سے پہلے پینٹ کیا جاتا ہے، اور foregroundPainter بعد میں۔ یہ CustomPaint کو آرائشی پس منظر یا اوورلے والے کنٹینر کے طور پر استعمال کرنے کی اجازت دیتا ہے بغیر اندر عام ویجیٹس لگانے کی صلاحیت کھوئے۔
Canvas Flutter میں مرکزی ڈرائنگ آبجیکٹ ہے، جو گرافکس سطح کو سمیٹتا ہے۔ یہ تقریباً 40 ڈرائنگ طریقے فراہم کرتا ہے: drawLine، drawCircle، drawRect، drawPath، drawArc، drawImage، drawPicture، drawPoints اور دیگر۔ Flutter میں Canvas Android Canvas میں اسی طرح کے کلاس سے مطابقت رکھتا ہے لیکن Dart آرکیٹیکچر اور Flutter Engine کے مطابق ڈھالا گیا ہے۔ Canvas ایک کوآرڈینیٹ سسٹم میں کام کرتا ہے جہاں اصل نقطہ (0, 0) اوپری بائیں کونہ ہے۔
Paint — paint() طریقہ سے الجھن نہ کریں — ایک کلاس ہے جو ڈرائنگ اسٹائل کی وضاحت کرتا ہے: رنگ (color)، لائن کی موٹائی (strokeWidth)، اسٹائل (style: fill یا stroke)، شفافیت، ملاوٹ کا طریقہ (blendMode) اور اینٹی ایلیاسنگ ماسک۔ Paint ڈرائنگ سے پہلے ترتیب دیا جاتا ہے اور Canvas کے طریقوں میں بھیجا جاتا ہے۔ اہم: Paint ایک قابل تبدیلی آبجیکٹ ہے، لیکن اسے ایک بار بنا کر صرف ضروری پیرامیٹرز تبدیل کرکے دوبارہ استعمال کرنے کی سفارش کی جاتی ہے۔
| Canvas طریقہ | مقصد | پیرامیٹرز |
|---|---|---|
| drawCircle | دائرہ کھینچنا | Offset center, double radius, Paint paint |
| drawLine | لکیر کا ٹکڑا کھینچنا | Offset p1, Offset p2, Paint paint |
| drawRect | مستطیل کھینچنا | Rect rect, Paint paint |
| drawPath | صوابدیدی راستہ کھینچنا | Path path, Paint paint |
| drawArc | قوس یا سیکٹر کھینچنا | Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint |
| drawRRect | گول کونوں والا مستطیل | RRect rrect, Paint paint |
| drawOval | بیضوی کھینچنا | Rect rect, Paint paint |
| drawImage | Image سے تصویر کھینچنا | Image image, Offset p, Paint paint |
Canvas کوآرڈینیٹ سسٹم کی تبدیلیوں کو سپورٹ کرتا ہے: translate(double dx, double dy) اصل نقطہ کو منتقل کرتا ہے؛ rotate(double radians) سسٹم کو گھماتا ہے؛ scale(double sx, double sy) پیمانہ بدلتا ہے؛ skew(double sx, double sy) جھکاتا ہے۔ تبدیلی سے پہلے، save() کے ذریعے Canvas کی حالت محفوظ کرنے اور مکمل ہونے کے بعد restore() کے ذریعے بحال کرنے کی سفارش کی جاتی ہے۔ یہ بعد کی paint کالز میں تبدیلیوں کے جمع ہونے سے روکتا ہے۔
paint(Canvas canvas, Size size) لازمی طریقہ ہے جو تمام ڈرائنگ منطق کو لاگو کرتا ہے۔ canvas پیرامیٹر ڈرائنگ کے لیے گرافکس سطح ہے۔ size پیرامیٹر ڈرائنگ کے لیے مختص کردہ علاقے کا سائز ہے (منطقی پکسلز میں چوڑائی اور اونچائی)۔ Canvas پہلے سے ترتیب دیا گیا ہے تاکہ اصل نقطہ (0,0) ڈرائنگ ایریا کے اوپری بائیں کونے سے مماثل ہو۔ تمام کوآرڈینیٹ اس اصل نقطہ کے نسبت متعین کیے جاتے ہیں۔
shouldRepaint(covariant CustomPainter oldDelegate) ایک لازمی طریقہ ہے جو دوبارہ پینٹ کی فریکوئنسی کو بہتر بناتا ہے۔ Flutter ہر ویجیٹ ٹری اپ ڈیٹ پر shouldRepaint کو کال کرتا ہے جو CustomPainter کو متاثر کرتی ہے۔ اگر طریقہ false لوٹاتا ہے تو پہلے سے رینڈر شدہ تصویر درست مانی جاتی ہے اور paint کال نہیں ہوتی۔ عام نفاذ: return oldDelegate.someField != someField — دوبارہ پینٹ کی ضرورت کا تعین کرنے کے لیے پرانے اور نئے painter کے فیلڈز کا موازنہ۔
متحرک یا مسلسل بدلتی ہوئی تصاویر (گھڑیاں، لوڈنگ اشارے، چارٹ) کے لیے shouldRepaint کو ہر اسٹیٹ تبدیلی پر true لوٹانا چاہیے۔ اگر آبجیکٹ جامد ہے (لوگو، آئیکن، پس منظر) تو shouldRepaint پہلی رینڈرنگ کے بعد false لوٹاتا ہے۔ Flutter خود بخود paint کے نتیجے کو کیش کرتا ہے اور صرف اس وقت دوبارہ پینٹ کرتا ہے جب سائز، شفافیت یا shouldRepaint کال تبدیل ہو۔
آئیے GaugePainter بنائیں — متحرک بھرائی کے ساتھ ایک کسٹم سرکلر گیج۔ CustomPainter موجودہ قدر (0.0 سے 1.0 تک پیشرفت) قبول کرتا ہے اور گرڈیئنٹ کے ساتھ ایک قوس کھینچتا ہے۔ shouldRepaint طریقہ اصلاح کے لیے پیشرفت کی قدر کا موازنہ کرتا ہے۔ CustomPaint کو ڈرائنگ ایریا کو محدود کرنے کے لیے size پیرامیٹر کے ساتھ ویجیٹ ٹری میں رکھا جاتا ہے۔
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;
}
}
مثال Path استعمال کرتے ہوئے ایک موج چارٹ کھینچتی ہے۔ Path لکیروں، بیزیئر منحنی خطوط اور قوسوں سے صوابدیدی شکلیں بنانے کی اجازت دیتا ہے۔ شکل بنانے کے بعد، اسے رنگ سے بھرا جاتا ہے یا لکیر سے خاکہ بنایا جاتا ہے۔ مثال میں، چارٹ ایک موج کی شکل نقل کرنے والے پوائنٹس کی ایک صف سے بنایا گیا ہے۔
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 (پس منظر) CustomPaint کے چائلڈ ویجیٹ سے پہلے پینٹ کیا جاتا ہے۔ اگر CustomPaint کا چائلڈ ویجیٹ (child پیرامیٹر) ہے تو پس منظر painter اس کے نیچے دکھایا جاتا ہے۔ یہ سجاوٹ کے لیے موزوں ہے: پیشرفت بار، پس منظر کے پیٹرن، واٹر مارک۔ foregroundPainter چائلڈ ویجیٹ کے بعد پینٹ کیا جاتا ہے، یعنی اس کے اوپر۔ یہ اوورلے کے لیے استعمال ہوتا ہے: ہائی لائٹس، ماسک، پیمائش کرنے والے رولر، تشریحات۔
painter اور foregroundPainter کے درمیان فرق Z-ترتیب کے لیے اہم ہے۔ اگر آپ کو مواد کے پیچھے کچھ کھینچنا ہے — painter استعمال کریں۔ اگر مواد کے اوپر — foregroundPainter استعمال کریں۔ بعض صورتوں میں، آپ دونوں painter کو ایک ساتھ استعمال کر سکتے ہیں: painter کے ذریعے پس منظر، foregroundPainter کے ذریعے اوورلے لیبل۔ یہ مختلف گرافک تہوں کی منطق کو دو علیحدہ CustomPainter کلاسز میں تقسیم کرنے کی اجازت دیتا ہے۔
paint طریقہ میں size پیرامیٹر دونوں painter کے لیے یکساں ہے اور CustomPaint کے لیے مختص کردہ علاقے کے سائز سے متعین ہوتا ہے۔ سائز CustomPaint میں size پیرامیٹر کے ذریعے واضح طور پر یا پیرنٹ کنٹینر کے ذریعے ضمنی طور پر سیٹ کیا جا سکتا ہے۔ اگر سائز Size.zero ہے تو paint کال نہیں ہوتی — Flutter صفر سائز والے علاقے کے لیے رینڈرنگ چھوڑ دیتا ہے۔ عنصر کے ظہور کو متحرک کرتے وقت اس رویے پر غور کیا جانا چاہیے۔
CustomPainter کارکردگی کا بنیادی اصول یہ ہے کہ paint طریقہ کے اندر آبجیکٹ نہ بنائیں۔ Canvas، Paint، Path، Rect، Offset اور دیگر ڈرائنگ آبجیکٹ CustomPainter کنسٹرکٹر میں بنائے جانے چاہئیں یا کیش کیے جانے چاہئیں۔ paint کے اندر آبجیکٹ بنانے سے ہر فریم پر مختص ہوتا ہے، جو 60 فریم فی سیکنڈ پر بار بار کوڑا کرکٹ جمع کرنے اور اینیمیشن جھٹکے (jank) کا سبب بنتا ہے۔
Path کیشنگ بار بار استعمال ہونے والے پیچیدہ راستوں کی رینڈرنگ کو نمایاں طور پر تیز کرتی ہے۔ اگر راستہ فریموں کے درمیان تبدیل نہیں ہوتا (مثال کے طور پر، پس منظر کا گرڈ)، تو Path کو ایک بار بنائیں اور اسے کئی بار دوبارہ استعمال کریں۔ RepaintBoundary ایک ریپر ویجیٹ ہے جو اوپری ویجیٹ ٹری تبدیل ہونے پر CustomPainter کو دوبارہ پینٹ ہونے سے روکتا ہے۔ اگر CustomPainter جامد ہے تو علیحدگی کے لیے اسے RepaintBoundary میں لپیٹ دیں۔
بار بار دوبارہ پینٹ والے متحرک CustomPainter کے لیے، shouldRepaint کے ساتھ Ticker یا AnimationController استعمال کریں جو متحرک قدر کی ہر تبدیلی پر true لوٹاتا ہے۔ shouldRepaint میں متحرک فیلڈ کا موازنہ کرنا بہتر ہے بجائے اس کے کہ بغیر ضرورت کے ہر فریم پر دوبارہ پینٹ کیا جائے۔ پیچیدہ گرڈیئنٹس کے لیے Rect کیشنگ: اگر گرڈیئنٹ رنگ تبدیل نہیں ہوتے تو آپ Paint.shader کے ذریعے Shader کو کیش کر سکتے ہیں۔
اکثر پوچھے گئے سوالات
CustomPainter ایک معاہدہ کلاس ہے جو یہ طے کرتا ہے کہ Canvas پر کیا اور کیسے کھینچنا ہے۔ Canvas خود ڈرائنگ آبجیکٹ ہے، جو طریقے فراہم کرتا ہے (drawCircle، drawLine وغیرہ)۔ CustomPainter paint طریقے کے ذریعے Canvas حاصل کرتا ہے اور اس کے استعمال کو کنٹرول کرتا ہے۔ ایک Canvas مختلف اوقات میں مختلف CustomPainter کے ذریعے استعمال کیا جا سکتا ہے۔
نہیں، CustomPainter کا CustomPaint کے بغیر کوئی مطلب نہیں۔ CustomPaint وہ ویجیٹ ہے جو CustomPainter کے paint طریقے کو کال کرتا ہے اور نتیجہ اسکرین پر دکھاتا ہے۔ CustomPaint کے بغیر Canvas نہیں بنے گا اور paint عمل نہیں ہوگا۔ تاہم، آپ Canvas کے ساتھ اپنا RenderObject لاگو کر سکتے ہیں، لیکن یہ ایک نچلی سطح کا طریقہ ہے۔
کنسٹرکٹر پیرامیٹرز کے ذریعے متحرک قدر CustomPainter میں منتقل کرتے ہوئے Ticker کے ساتھ AnimationController استعمال کریں۔ جب قدر تبدیل ہو تو CustomPaint پر مشتمل ویجیٹ میں setState() کال کریں، جو shouldRepaint کال کو متحرک کرتا ہے۔ اگر shouldRepaint true لوٹاتا ہے تو سسٹم نئے اینیمیشن فریم کے لیے paint کال کرتا ہے۔
وجہ تقریباً ہمیشہ shouldRepaint کا false لوٹانا ہے۔ چیک کریں کہ oldDelegate کے فیلڈز نئی اقدار سے درست طریقے سے موازنہ کیے جا رہے ہیں۔ اگر قابل تبدیلی آبجیکٹ استعمال کیا جائے تو shouldRepaint تبدیلیاں نہیں پکڑ سکتا کیونکہ آبجیکٹ کا حوالہ وہی رہتا ہے۔ درست موازنہ کے لیے == یا ناقابل تبدیلی آبجیکٹ استعمال کریں۔
ہاں، CustomPainter پیچیدہ فہرستوں میں کسٹم سجاوٹ رینڈر کرنے کے لیے استعمال ہوتا ہے۔ تاہم، نوٹ کریں کہ آئٹمز کو دوبارہ ترتیب دیتے وقت ListView کے اندر RepaintBoundary ہر آئٹم کی رینڈرنگ کو کیش کرتا ہے۔ CustomPainter ہلکا ہونا چاہیے اور ڈریگ اینیمیشن کو سست نہ کرنے کے لیے paint میں بھاری آپریشنز نہیں ہونے چاہئیں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں