الودجات المخصصة والرسم المخصص

مقدمة إلى CustomPainter

15 دقيقة الدرس 3 من 12

مقدمة إلى CustomPainter

تغطي ودجات Flutter المدمجة نطاقاً واسعاً من أنماط واجهة المستخدم، لكن في بعض الأحيان تحتاج إلى رسم شيء لا يوفره أي ودجت — مخطط مخصص، أو مؤشر تقدم فريد، أو شكل زخرفي، أو كائن لعبة. لهذه الحالات، تكشف Flutter عن واجهة برمجية منخفضة المستوى للرسم على Canvas من خلال الفئة CustomPainter. عبر اشتقاق فئة من CustomPainter وإرفاقها بودجت CustomPaint، تحصل على وصول مباشر إلى سطح العرض ويمكنك رسم أي شيء يمكن وصفه رياضياً.

عقد CustomPainter

يجب على كل رسّام مخصص تنفيذ طريقتين فقط:

  • paint(Canvas canvas, Size size) — تستدعيها الإطار في كل مرة يحتاج فيها الودجت إلى الرسم. تصدر أوامر الرسم على كائن Canvas؛ وتخبرك Size بحجم المساحة المتاحة.
  • shouldRepaint(CustomPainter oldDelegate) — تُستدعى قبل كل إعادة رسم محتملة. أعد true إذا كانت بيانات الرسّام الجديد مختلفة عن القديم وكانت إعادة الرسم ضرورية؛ أعد false لتخطي إعادة الرسم المكلفة.
ملاحظة: shouldRepaint هي ضمانة للأداء. إعادة true دون شرط آمنة لكنها مهدِرة — كل إعادة بناء للأب ستطلق إعادة رسم كاملة للـ Canvas. قارن دائماً الحقول ذات الصلة وأعد false عندما لا يتغير شيء.

أول CustomPainter لك

أبسط رسّام ممكن يشتق من CustomPainter، ويتجاوز الطريقتين المطلوبتين، ويرسم دائرة مملوءة في مركز الـ Canvas.

CustomPainter بسيط للغاية

import 'package:flutter/material.dart';

class CirclePainter extends CustomPainter {
  final Color color;
  final double radius;

  const CirclePainter({required this.color, required this.radius});

  @override
  void paint(Canvas canvas, Size size) {
    // تحديد كيفية ملء الشكل
    final paint = Paint()
      ..color = color
      ..style = PaintingStyle.fill;

    // رسم دائرة في مركز المساحة المتاحة
    final centre = Offset(size.width / 2, size.height / 2);
    canvas.drawCircle(centre, radius, paint);
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) {
    // إعادة الرسم فقط إذا تغير المظهر فعلياً
    return oldDelegate.color != color || oldDelegate.radius != radius;
  }
}

إرفاق الرسّام بواسطة CustomPaint

لا يُوضع نسخة CustomPainter مباشرةً في شجرة الودجات. بل تلفها في ودجت CustomPaint، الذي يخصص Canvas للرسّام ويدير دورة حياته.

استخدام CustomPaint في شجرة الودجات

class PainterDemo extends StatelessWidget {
  const PainterDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Custom Painter Demo')),
      body: Center(
        child: CustomPaint(
          // تحديد حجم صريح للـ Canvas
          size: const Size(300, 300),
          painter: CirclePainter(
            color: Colors.deepPurple,
            radius: 100,
          ),
          // ودجات الأبناء تُعرض فوق المحتوى المرسوم
          child: const Center(
            child: Text(
              'Hello Canvas!',
              style: TextStyle(color: Colors.white, fontSize: 18),
            ),
          ),
        ),
      ),
    );
  }
}
نصيحة: يقبل CustomPaint كلاً من painter (يُرسم خلف الابن) وforegroundPainter (يُرسم أمام الابن). استخدم foregroundPainter عندما تريد أن يتراكب المحتوى المرسوم فوق ودجت الابن، مثل شارة أو حلقة تمييز.

كائن Paint — فرشاة الرسم

قبل إصدار أي أمر رسم على الـ Canvas، تهيئ كائن Paint الذي يعمل كفرشاة قابلة للضبط. أكثر الخصائص استخداماً هي:

  • color — لون التعبئة أو الحدود.
  • stylePaintingStyle.fill يملأ الشكل؛ PaintingStyle.stroke يرسم الحدود فقط.
  • strokeWidth — سماكة الحدود، بالبكسل المنطقي.
  • isAntiAlias — تنعيم حواف المنحنيات (الافتراضي true).
  • shader — تطبيق تدرجات أو أنماط صور بدلاً من لون ثابت.

أوامر الرسم الأساسية في Canvas

تكشف الفئة Canvas عن واجهة برمجية غنية. أكثر الطرق أهمية للمبتدئين هي:

  • canvas.drawCircle(Offset centre, double radius, Paint paint)
  • canvas.drawRect(Rect rect, Paint paint)
  • canvas.drawLine(Offset p1, Offset p2, Paint paint)
  • canvas.drawPath(Path path, Paint paint) — للأشكال التعسفية.
  • canvas.drawOval(Rect rect, Paint paint)
  • canvas.drawRRect(RRect rrect, Paint paint) — مستطيلات بزوايا مستديرة.
تحذير: تبدأ إحداثيات الـ Canvas من الزاوية العلوية اليسرى لودجت CustomPaint، مع زيادة x نحو اليمين وزيادة y نحو الأسفل. إذا ظهر رسمك مقصوصاً أو خارج الشاشة، تحقق من أنك تستخدم الأصل الصحيح وأن شكلك يناسب Size المتاحة.

إطلاق إعادة الرسم مع AnimationController أو setState

الرسّام الثابت مفيد، لكن القوة الحقيقية تظهر عند دمج CustomPainter مع AnimationController أو مع StatefulWidget يمرر بيانات جديدة إلى مُنشئ الرسّام. في كل مرة يستدعي فيها الأب setState بقيمة جديدة، تتحقق Flutter من shouldRepaint، وإذا أعادت true، تستدعي paint مرة أخرى بالبيانات المحدّثة — مما ينتج رسومات ناعمة وتفاعلية.

ملخص

لرسم رسومات مخصصة في Flutter، تشتق فئة من CustomPainter، وتنفذ paint() لإصدار أوامر Canvas، وتنفذ shouldRepaint() للتحكم في توقيت إعادة الرسم. يُرفق الرسّام بشجرة الودجات عبر ودجت CustomPaint، الذي يوفر Canvas بحجم صحيح. يعمل كائن Paint كفرشاتك القابلة للضبط، وتكشف واجهة Canvas البرمجية عن مجموعة غنية من أوليات الرسم. يُشكّل هذا الأساس كل شيء بدءاً من الزخارف البسيطة وصولاً إلى الرسومات التفاعلية المخصصة بالكامل في Flutter.