شجرة RenderObject: ما هي، مبادئ العرض ودورها في Flutter

المؤلف: IT Sectr نُشر: 2026-07-01 وقت القراءة: 9 دق

شجرة RenderObject هي المستوى الثالث في التسلسل الهرمي لـ Flutter، وهي المسؤولة عن العرض الفعلي للواجهة على الشاشة. كل عقدة في هذه الشجرة تقوم بحساب الأحجام (layout) والرسم (painting)، محولة تكوين الويجتات إلى بكسلات. بخلاف Widget Tree، فإن شجرة RenderObject تُنشأ مرة واحدة وتتحدث فقط عند تغير الهندسة أو المظهر. وفقًا لـ مرجع API Flutter، 2025، فإن كفاءة شجرة RenderObject تحدد مباشرة سلاسة الرسوم المتحركة وسرعة استجابة التطبيق.

النقاط الرئيسية

  • شجرة RenderObject هي تسلسل هرمي من كائنات العرض التي تحسب الأحجام وترسم الواجهة على الشاشة.
  • كل RenderObject ينفذ مرحلتين: layout (تحديد الأحجام والمواقع) و paint (رسم البكسلات).
  • شجرة RenderObject تُنشأ من Element Tree وتتزامن معه عبر العناصر.
  • RepaintBoundary يعزل جزءًا من الشجرة للإعادة المحلية دون المساس بالشاشة كاملة.
  • الأداء لشجرة RenderObject يعتمد على العمق، عدد الطبقات وتكرار إعادة الرسم.

ما هي شجرة RenderObject في Flutter؟

شجرة RenderObject هي بنية هرمية من كائنات العرض التي تعمل كتمثيل فعلي للواجهة في الذاكرة. كل RenderObject يعرف حجمه وموقعه على الشاشة وكيفية رسم نفسه. بخلاف الويجتات، التي خفيفة ويمكن إعادة إنشائها مئات المرات في الثانية، فإن RenderObject هو كائن ثقيل يملك وصولًا مباشرًا إلى خط الأنابيب الرسومي.

من التكوين إلى العرض

يمر المسار من الويجت إلى البكسلات بثلاث مراحل: يصف Widget Tree ما يجب أن يكون على الشاشة؛ يدير Element Tree دورة الحياة؛ وتقوم شجرة RenderObject بالعمل الفعلي. كل RenderObjectWidget (مثل Padding، Transform، CustomPaint) ينشئ RenderObject يتم إضافته إلى شجرة العرض. RenderBox هو النوع الأكثر شيوعًا من RenderObject، ويستخدم في 99% من الويجتات القياسية.

معمارية RenderObject

RenderObject هو صف مجرد يحدد واجهة التفاعل للتنسيق والرسم. يحتوي على مراجع لـ RenderObject الأب والكائنات التابعة، وكذلك طرق مجردة: performLayout، paint و hitTest. كل RenderObject محدد ينفذ هذه الطرق وفقًا لسلوكه: RenderFlex يوزع المساحة بين العناصر التابعة، RenderImage يعرض صورة، RenderParagraph يعرض نصًا.

dart
abstract class RenderObject {
  RenderObject? parent;
  Constraints constraints;
  ParentData? parentData;
  bool _needsLayout = true;
  bool _needsPaint = true;

  void performLayout();
  void paint(PaintingContext context, Offset offset);
}

في هذه البنية المبسطة، يحتوي RenderObject على العلامات _needsLayout و _needsPaint، التي تُعلم الكائن كمحتاج للتحديث. عندما تتغير Widget Tree، تُعلم Element Tree كائنات RenderObject المقابلة كـ «متسخة» للتنسيق أو الرسم، وفي الإطار التالي ينفذ Flutter فقط العمليات الضرورية.

مرحلتا RenderObject: layout و paint

كل RenderObject ينفذ مرحلتين رئيسيتين: layout (تحديد الأحجام والمواقع) و paint (الرسم). تتنفذ هذه المراحل بترتيب صارم: أولًا layout للشجرة بأكملها، ثم paint. يمكن تخطي مرحلة paint إذا لم يتغير layout، مما يوفر موارد GPU.

مرحلة layout: القيود والأحجام

أثناء layout، ينقل Flutter القيود (constraints) من أعلى لأسفل عبر شجرة RenderObject. يحدد كل أب أدنى وأقصى عرض وارتفاع للعنصر التابع. يحسب العنصر التابع حجمه ضمن هذه القيود ويعيده إلى الأب. يطلق على هذه العملية اسم «المرور التنازلي» (نقل القيود لأسفل) و«المرور التصاعدي» (نقل الأحجام لأعلى).

  • BoxConstraints هو أكثر أنواع القيود شيوعًا: minWidth، maxWidth، minHeight، maxHeight.
  • SliverConstraints يستخدم في ScrollView للافتراضية: يضيف معلومات التمرير إلى القيود القياسية.
  • RenderSliverMultiBoxAdaptor يدير layout للقوائم الافتراضية، منشئًا RenderObjects فقط للعناصر المرئية.

مرحلة paint: العرض على الشاشة

بعد اكتمال layout، ينفذ Flutter عملية paint — رسم كل RenderObject. ينشئ الأب PaintingContext، يمرره إلى الكائنات التابعة ويحدد الإزاحة (offset). يرسم كل RenderObject نفسه على Canvas باستخدام الأوليات الرسومية: المستطيلات، الدوائر، النصوص، الصور والتحويلات. يتفاعل Canvas مباشرة مع Skia أو Impeller.

وسم العقد المتسخة

يستخدم Flutter آلية العقد المتسخة لتقليل العمل. عندما يتغير حجم RenderObject واحد فقط، لا يعيد Flutter حساب layout للشجرة بأكملها — بل يسم العقدة المتغيرة وأسلافها حتى أقرب RenderBox بحجم ثابت. بالمثل، عندما يتغير المظهر، يتم وسم paint فقط دون إعادة layout.

أنواع RenderObject في Flutter

Flutter يوفر عدة فئات من RenderObject لمهام مختلفة: RenderBox للعناصر المستطيلة القياسية، RenderSliver للمناطق القابلة للتمرير، و RenderObjects مخصصة للعرض غير القياسي عبر CustomPainter. كل نوع محسن لدوره في شجرة العرض.

RenderBox وأنواعه الفرعية

RenderBox هو الصف الأساسي لعناصر الواجهة المستطيلة. تشمل أنواعه الفرعية: RenderPadding (يضيف حشوات)، RenderTransform (يطبق تحويلات)، RenderFlex (ينفذ Row و Column)، RenderStack (يطبق عناصر متطابقة)، RenderImage (يعرض صوراً)، RenderParagraph (يعرض نصًا). كل نوع فرعي يغطي performLayout و paint لمنطقه الخاص.

RenderSliver والافتراضية

RenderSliver هو نوع من RenderObject للعمل مع المناطق القابلة للتمرير. بخلاف RenderBox، يستخدم Sliver نوع SliverConstraints، الذي يتضمن معلومات حول المنظر (viewport). RenderSliverList و RenderSliverGrid يُنشئان RenderObjects فقط للعناصر التي تظهر في المنظر، مما يتيح التعامل مع قوائم تحتوي على ملايين الإدخالات.

RenderObject مخصص عبر CustomPainter

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

dart
class CirclePainter extends CustomPainter {
  final Color color;

  CirclePainter({required this.color});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = color;
    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      size.width / 3,
      paint,
    );
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) =>
    oldDelegate.color != color;
}

في هذا المثال، يرسم CirclePainter دائرة على Canvas. تعيد طريقة shouldRepaint قيمة true فقط عند تغير اللون، مما يمنع إعادات الرسم غير الضرورية. يجب أن يكون CustomPainter خفيفًا قدر الإمكان — أجر جميع الحسابات الثقيلة خارج طرق paint.

شجرة RenderObject تُنشأ من Element Tree عبر آلية RenderObjectWidget. كل RenderObjectWidget (Padding، Transform، CustomPaint) يُنشئ RenderObjectElement، الذي بدوره يُنشئ ويدير RenderObject المقابل. يعمل العنصر كوسيط: ينقل التكوين من الويجت إلى RenderObject ويخبر RenderObject بالتغييرات.

إنشاء RenderObject من عنصر

عندما يتم تثبيت RenderObjectElement، يستدعي طريقة createRenderObject من الويجت الخاص به. يُنشئ الويجت مثال RenderObject ويعيده إلى العنصر. يُدرج العنصر RenderObject في شجرة RenderObject باستدعاء طريقة insertChildLayout من RenderObject الأب. تحدث هذه العملية فقط عند التثبيت الأول — في التحديثات اللاحقة، يقوم العنصر ببساطة تحديث معلمات RenderObject الموجود.

المزامنة عبر updateRenderObject

عندما تتغير تكوينة الويجت (على سبيل المثال، قيمة padding)، يستدعي العنصر طريقة updateRenderObject، التي تنقل التكوين الجديد إلى RenderObject الموجود. يسم RenderObject نفسه كـ «متسخ» للتنسيق أو الرسم، وفي الإطار التالي ينفذ الإطار التحديثات الضرورية.

إزالة RenderObject

عند إلغاء تثبيت عنصر، تستدعى طريقة unmount، التي تزيل RenderObject من شجرة RenderObject وتحرر الموارد. يتم استدعاء RenderObject.remove لفصله عن الأب، بعد ذلك يمكن جمع الكائن بواسطة مجمع القمامة. يضمن Flutter عدم وجود RenderObject معلق في الشجرة دون عنصر مقابل.

RepaintBoundary وعزل العرض

RepaintBoundary هو ويجت يُنشئ طبقة منفصلة لعرض محتواه. عندما يتغير المحتوى داخل RepaintBoundary، تتم إعادة رسم تلك الطبقة فقط، ويبقى باقي الشاشة دون تغيير. RepaintBoundary مفيد خاصة للرسوم المتحركة، مشغلات الفيديو، الرسوم البيانية التفاعلية والعناصر الأخرى التي تتحدث بشكل متكرر.

كيف يعمل RepaintBoundary

في جوهر RepaintBoundary يكمن RenderRepaintBoundary — RenderObject خاص يُنشئ PictureLayer منفصلاً. في أول عرض، يسجل RenderRepaintBoundary أوامر الرسم في هذه الطبقة. في التحديثات اللاحقة، إذا تغير المحتوى داخل RepaintBoundary فقط، يعيد Flutter رسم هذه الطبقة فقط وليس الشاشة بأكملها. تبقى الطبقات الأخرى دون تغيير وتُعاد استخدامها.

متى تستخدم RepaintBoundary

ليس كل ويجت يحتاج RepaintBoundary. استخدمه عندما يتحدث جزء من الواجهة بتردد عالي (60 FPS أو أكثر) بينما باقي الشاشة ثابت. أمثلة نموذجية: مؤشر تحميل متحرك، مشغل فيديو، Canvas لعبة، CustomPainter بإعادات رسم متكررة. للنص الثابت أو الأزرار، RepaintBoundary زائد ويزيد فقط في استهلاك الذاكرة.

السيناريوالتوصيةالسبب
رسوم متحركةلف في RepaintBoundaryيعزل المنطقة المتحدثة بكثرة
قائمة قابلة للتمريرغير مطلوبListView تستخدم الطبقات تلقائيًا
نص ثابتغير مطلوبلا توجد إعادات رسم متكررة
CustomPainterموصى بهإعادات رسم رسومية متكررة

تحسين أداء شجرة RenderObject

الأداء لشجرة RenderObject يعتمد على عدد العقد، عمق الشجرة وتكرار إعادة الرسم. توفر Flutter DevTools (علامة التبويب «Rendering») التحليل الفوري لشجرة RenderObject: عدد إعادات الرسم، وقت layout و paint، عدد الطبقات وأحجامها. يساعد التحليل المنتظم في تحديد الاخناقات.

تجنب الإفراط في Overdraw

Overdraw هو موقف يتم فيه رسم بكسل واحد عدة مرات في الإطار الواحد. على سبيل المثال، عندما يتداخل ويجت شفاف مع آخر، ترسم GPU كلتا الطبقتين. استخدم علامات غير شفافة (Container باستخدام color بدل decoration) للعناصر غير الشفافة لكي يتجاوز Flutter الطبقات غير المرئية. وفقًا لفريق Flutter، قل ال overdraw يمكن أن يقلل وقت paint بنسبة تصل إلى 30%.

قلل عدد الطبقات

كل RepaintBoundary وبعض الويجتات (Opacity، ClipRRect، Transform) تُنشئ طبقة منفصلة (PictureLayer). الطبقات الكثيرة تزيد وقت التركيب. استخدم التجميع: بدلاً من ويجتات Opacity متعددة على عناصر فردية، طبق Opacity واحدًا على الحاوية. بدلاً من ClipRRect على كل عنصر، استخدم ClipRRect على الحاوية المشتركة.

استخدم منشئات const

عندما يتم تصريح ويجت كـ const، يعلم Flutter أن تكوينته لن تتغير ويمكنه إعادة استخدام RenderObject المقابل دون إعادة إنشائه. تقلل المنشئات const عبء مجمع القمامة وتسرع أول إطار. استخدم const للويجتات ذات المعلمات الثابتة: الأيقونات، العناوين، العناصر الزخرفية.

dart
const Text("عنوان", style: TextStyle(fontSize: 24));
const Icon(Icons.home, color: Colors.blue);
const SizedBox.shrink();

الأسئلة الشائعة

ما الفرق بين شجرة RenderObject و Widget Tree؟

Widget Tree هي تكوين خفيف للواجهة يتم إعادة إنشائه في كل إعادة بناء. شجرة RenderObject هي شجرة ثقيلة من كائنات العرض تبقى وتتحدث فقط عند تغير الهندسة أو المظهر.

كيف يمكنني رؤية شجرة RenderObject في المصحح؟

استخدم Flutter DevTools — علامة التبويب «Rendering». سترى طبقات العرض، وقت layout و paint، ومعلومات مفصلة عن كل RenderObject: الأحجام، القيود وعلامات الاحتياج للتحديث.

ما هو RenderObject متسخ؟

Dirty هو RenderObject معلم عليه بأنه يحتاج تحديثًا. يتم تعيين العلامة _needsLayout أو _needsPaint عند تغيير التكوينة، وينفذ Flutter layout أو paint لهذه العقدة في الإطار التالي.

هل يمكنني إنشاء RenderObject خاص بي؟

نعم، أنشئ صفًا فرعيًا من RenderBox وغطي طريقتي performLayout و paint. استخدم RenderObjectWidget لتضمين RenderObject مخصص في Widget Tree. هذه تقنية متقدمة للعرض غير القياسي.

كيف يؤثر RepaintBoundary على شجرة RenderObject؟

RepaintBoundary يخلق RenderRepaintBoundary، الذي يعزل جزءًا من شجرة RenderObject في طبقة منفصلة. عند تغير المحتوى، تتم إعادة رسم تلك الطبقة فقط، ويبقى باقي الشجرة دون تغيير.

الخلاصة

  • شجرة RenderObject هي المستوى الثالث في معمارية Flutter، المسؤولة عن layout و paint لكل عنصر واجهة.
  • كل RenderObject ينفذ مرحلتين: layout (حساب الأحجام والمواقع) و paint (رسم البكسلات عبر Canvas).
  • RenderBox هو النوع الأساسي للعناصر القياسية، RenderSliver لتطبيع القوائم القابلة للتمرير.
  • شجرة RenderObject تتزامن مع Element Tree عبر RenderObjectElement، الذي يُنشئ ويحدث RenderObjects.
  • RepaintBoundary يعزل جزءًا من الشجرة في طبقة منفصلة، ممنعًا إعادة رسم الشاشة كاملة عند التغييرات المحلية.
  • الأداء يعتمد على عمق الشجرة، عدد الطبقات، overdraw واستخدام المنشئات const.
  • Flutter DevTools توفر أدوات لتحليل شجرة RenderObject: وقت layout، وقت paint، عدد الطبقات وعدد إعادات الرسم.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا