مناطق القص ومسارات القطع
مناطق القص ومسارات القطع
يرسم Flutter كل ودجت داخل صندوق إحاطة مستطيل بشكل افتراضي. يتيح لك القص (Clipping) تقييد هذا الرسم بشكل اعتباطي — مستطيل ذو حواف مدورة، أو دائرة، أو نجمة، أو أي مسار مخصص — دون التأثير على هندسة التخطيط نفسها. تصبح المنطقة المقصوصة شفافة ببساطة؛ والبكسلات الخارجة عن حدود القص لا تُرسم.
يأتي Flutter مزوداً بأربعة ودجات قص مخصصة (ClipRect، وClipRRect، وClipOval، وClipPath)، كما يوفر canvas.clipPath() داخل CustomPainter لتحكم أدق أثناء الرسم المخصص.
SizedBox أو OverflowBox.ClipRect — قص مستطيل
يقيّد ClipRect الرسم بالمستطيل المحيط بالودجت نفسه. يُستخدم بشكل رئيسي لمنع تأثيرات الفيض — مثلاً عندما تنزلق ودجت فرعية من خارج حدود الأب أثناء الرسوم المتحركة، أو عندما تتجاوز ودجت داخل Stack حدودها.
ClipRect لإخفاء الفيض
ClipRect(
child: Align(
alignment: Alignment.topCenter,
heightFactor: 0.5, // عرض النصف العلوي فقط
child: Image.network(
'https://example.com/photo.jpg',
width: 300,
height: 200,
fit: BoxFit.cover,
),
),
)
ClipRRect — قص مستطيل بحواف مدورة
يقص ClipRRect العنصر الفرعي بمستطيل ذي حواف مدورة يُحدَّد بـ BorderRadius. هذه هي الطريقة المعيارية لمنح أي ودجت — صور، حاويات، بطاقات — حواف مدورة دون الحاجة لتغليفها في Container مع BoxDecoration.
ClipRRect لصورة ذات حواف مدورة
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.network(
'https://example.com/avatar.jpg',
width: 120,
height: 120,
fit: BoxFit.cover,
),
)
ClipOval — قص دائري أو بيضاوي
يقص ClipOval العنصر الفرعي بأكبر قطع ناقص يتناسب مع صندوق الإحاطة. عندما يكون الصندوق مربعاً، تكون النتيجة دائرة مثالية — وهو الشكل الكلاسيكي لصورة الملف الشخصي.
ClipOval لصورة ملف شخصي دائرية
ClipOval(
child: Image.network(
'https://example.com/profile.jpg',
width: 80,
height: 80,
fit: BoxFit.cover,
),
)
ClipOval على ClipRRect(borderRadius: BorderRadius.circular(radius)) عندما تريد دائرة حقيقية، لأن ClipOval يتكيف تلقائياً مع حجم الودجت الفعلي دون الحاجة لتحديد قيمة نصف القطر يدوياً.ClipPath — قص بشكل اعتباطي
ClipPath هو أقوى ودجت قص. يقبل CustomClipper<Path> تعيد دالة getClip() فيه أي كائن Path مبني من خطوط وأقواس ومنحنيات مكعبة أو عمليات Path.combine().
ClipPath قطري مخصص
class DiagonalClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final path = Path();
path.lineTo(0, size.height - 60); // حافة أسفل اليسار
path.lineTo(size.width, size.height); // أسفل اليمين
path.lineTo(size.width, 0); // أعلى اليمين
path.close();
return path;
}
@override
bool shouldReclip(DiagonalClipper oldClipper) => false;
}
// الاستخدام
ClipPath(
clipper: DiagonalClipper(),
child: Container(
height: 200,
color: Colors.deepPurple,
child: const Center(
child: Text(
'ترويسة قطرية',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
)
shouldReclip() القيمة true كلما اعتمد شكل القص على بيانات قابلة للتغيير (مثل قيمة رسوم متحركة). إعادة false بشكل غير مشروط صحيح فقط حين يكون شكل القص ثابتاً.canvas.clipPath() داخل CustomPainter
عندما تحتاج لتطبيق قص كجزء من تسلسل رسم مخصص أشمل — مثلاً لتقييد تدرج لوني أو شكل مرسوم معقد — استدعِ canvas.clipPath(path) (أو canvas.clipRect() / canvas.clipRRect()) مباشرةً داخل CustomPainter.paint(). يؤثر القص على جميع عمليات الرسم اللاحقة على تلك اللوحة حتى تحفظ/تستعيد حالة اللوحة.
canvas.clipPath في CustomPainter
class WavePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// بناء مسار قص على شكل موجة
final clipPath = Path();
clipPath.lineTo(0, size.height * 0.75);
clipPath.quadraticBezierTo(
size.width * 0.25, size.height,
size.width * 0.5, size.height * 0.75,
);
clipPath.quadraticBezierTo(
size.width * 0.75, size.height * 0.5,
size.width, size.height * 0.75,
);
clipPath.lineTo(size.width, 0);
clipPath.close();
// تطبيق القص — جميع عمليات الرسم أدناه تحترم هذا الحد
canvas.clipPath(clipPath);
// رسم مستطيل متدرج؛ سيُقنَّع بشكل الموجة
final rect = Offset.zero & size;
final paint = Paint()
..shader = const LinearGradient(
colors: [Color(0xFF6A11CB), Color(0xFF2575FC)],
).createShader(rect);
canvas.drawRect(rect, paint);
}
@override
bool shouldRepaint(WavePainter oldDelegate) => false;
}
اعتبارات الأداء
- تقبل جميع ودجات القص معاملاً
clipBehavior(الافتراضي:Clip.antiAlias). استخدمClip.hardEdgeلأقصى أداء حين لا تكون الحواف الناعمة ضرورية. - تغليف شجرة فرعية بقص يفرض إنشاء طبقة جديدة، مما يضيف تكلفة تركيب GPU. تجنب القصوص غير الضرورية في شجرات فرعية تُعاد بناؤها كثيراً.
- استخدم
RepaintBoundaryحول الودجات المقصوصة التي تُعاد رسمها كثيراً لعزل تكلفة إعادة الرسم.
الملخص
يمنحك نظام القص في Flutter تحكماً دقيقاً في حدود الودجات دون تغيير التخطيط. استخدم ClipRect لإخفاء الفيض، وClipRRect للحواف المدورة، وClipOval للدوائر والأشكال البيضاوية، وClipPath (مدعوم بـ CustomClipper<Path>) لأي شكل اعتباطي. للرسم المخصص الكامل، استدعِ canvas.clipPath() داخل CustomPainter.paint() لتقييد عمليات الرسم بالشكل الذي تحدده. دائماً اضبط shouldReclip() بشكل صحيح واختر سلوك Clip المناسب للتوازن بين جودة العرض وأداء التصيير.