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-এ সমস্ত ড্রইং অপারেশন executes করে। CustomPaint উইজেট paint কল পরিচালনা করে এবং ফলাফলকে উইজেট ট্রিতে একীভূত করে।
CustomPainter এবং Canvas-এর মাধ্যমে সরাসরি ড্রইংয়ের মধ্যে মূল পার্থক্য হলো রেন্ডারিং লজিক (paint) এবং পুনরায় আঁকার ট্রিগার (shouldRepaint)-এর পৃথকীকরণ। Flutter সিস্টেম প্রতিবার প্যারেন্ট উইজেট পরিবর্তিত হলে shouldRepaint কল করে। যদি shouldRepaint false রিটার্ন করে, তাহলে paint কল করা হয় না, যা সম্পদ সাশ্রয় করে। এই মেকানিজম RenderObject-এ shouldRebuild-এর মতো এবং Flutter আর্কিটেকচারের তিনটি ট্রি (Widget, Element, RenderObject) সহ অংশ।
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 দ্বারা ব্যবহার করা যেতে পারে।
না, CustomPaint ছাড়া CustomPainter-এর কোনো অর্থ নেই। CustomPaint হলো উইজেট যা CustomPainter-এর paint মেথড কল করে এবং ফলাফল স্ক্রিনে প্রদর্শন করে। CustomPaint ছাড়া, Canvas তৈরি হবে না এবং paint execute হবে না। তবে, আপনি Canvas-সহ নিজস্ব RenderObject ইমপ্লিমেন্ট করতে পারেন, কিন্তু এটি নিম্ন-স্তরের পদ্ধতি।
Ticker-সহ AnimationController ব্যবহার করুন, কন্সট্রাক্টর প্যারামিটারের মাধ্যমে অ্যানিমেটেড মান CustomPainter-এ পাঠান। মান পরিবর্তিত হলে, CustomPaint ধারণকারী উইজেটে setState() কল করুন, যা shouldRepaint কল ট্রিগার করে। যদি shouldRepaint true রিটার্ন করে, সিস্টেম নতুন অ্যানিমেশন ফ্রেমের জন্য paint কল করে।
কারণ প্রায় সবসময় shouldRepaint false রিটার্ন করছে। পরীক্ষা করুন যে oldDelegate ফিল্ড নতুন মানের সাথে সঠিকভাবে তুলনা করা হয়েছে কিনা। যদি পরিবর্তনযোগ্য অবজেক্ট ব্যবহার করা হয়, তাহলে shouldRepaint পরিবর্তন সনাক্ত নাও করতে পারে কারণ অবজেক্ট রেফারেন্স একই থাকে। সঠিক তুলনার জন্য == বা অপরিবর্তনীয় অবজেক্ট ব্যবহার করুন।
হ্যাঁ, CustomPainter জটিল তালিকায় কাস্টম সাজসজ্জা রেন্ডার করতে ব্যবহৃত হয়। তবে, মনে রাখবেন যে আইটেম পুনর্বিন্যাস করার সময়, ListView-এর ভিতরে RepaintBoundary প্রতিটি আইটেমের রেন্ডারিং ক্যাশ করে। CustomPainter হালকা হওয়া উচিত এবং ড্র্যাগ অ্যানিমেশন ধীর না করতে paint-এ ভারী অপারেশন থাকা উচিত নয়।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন