RenderObject Tree — سومین سطح سلسلهمراتب در Flutter است که مسئول رندرینگ واقعی رابط روی صفحه است. هر گره از این درخت محاسبه اندازهها (layout) و رسم (painting) را انجام میدهد و پیکربندی ویجتها را به پیکسل تبدیل میکند. برخلاف Widget Tree، RenderObject Tree یک بار ایجاد میشود و فقط با تغییر هندسه یا ظاهر بهروزرسانی میشود. بر اساس Flutter API Reference, 2025, کارایی RenderObject Tree به طور مستقیم روانی انیمیشنها و سرعت پاسخگویی برنامه را تعیین میکند.
نکات اصلی
RenderObject Tree — ساختار سلسلهمراتبی از اشیاء رندرینگ است که نمایش واقعی رابط در حافظه محسوب میشود. هر RenderObject اندازهها، موقعیت خود روی صفحه را میداند و قادر به رسم خود است. برخلاف ویجتها که سبکوزن هستند و میتوانند صدها بار در ثانیه بازسازی شوند، RenderObject — شیء سنگینی با دسترسی مستقیم به خط لوله گرافیکی است.
مسیر از ویجت به پیکسل سه مرحله را طی میکند: Widget Tree مشخص میکند چه چیزی روی صفحه باشد؛ Element Tree چرخه حیات را مدیریت میکند؛ RenderObject Tree کار واقعی را انجام میدهد. هر RenderObjectWidget (مانند Padding, Transform, CustomPaint) یک RenderObject ایجاد میکند که به درخت رندرینگ اضافه میشود. RenderBox — رایجترین نوع RenderObject است که برای ۹۹٪ ویجتهای استاندارد استفاده میشود.
RenderObject — کلاس انتزاعی است که رابطی برای layout و paint تعریف میکند. این کلاس شامل ارجاع به RenderObject والد و اشیاء فرزند، و همچنین متدهای انتزاعی: performLayout, paint و hitTest است. هر RenderObject مشخص این متدها را مطابق با رفتار خود پیادهسازی میکند: RenderFlex فضا را بین عناصر فرزند توزیع میکند، RenderImage تصویر را نمایش میدهد، RenderParagraph متن را نمایش میدهد.
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های مربوطه را به عنوان "کثیف" برای layout یا paint علامتگذاری میکند و در فریم بعدی Flutter فقط عملیات ضروری را انجام میدهد.
هر RenderObject دو فاز کلیدی را انجام میدهد: layout (تعیین اندازهها و موقعیتها) و paint (رسم). این فازها به ترتیب دقیق اجرا میشوند: ابتدا layout برای کل درخت، سپس paint. اگر layout تغییر نکرده باشد، فاز paint میتواند حذف شود که باعث صرفهجویی در منابع GPU میشود.
در طول layout، Flutter محدودیتها (constraints) را از بالا به پایین در RenderObject Tree ارسال میکند. هر والد برای شیء فرزند حداقل و حداکثر عرض و ارتفاع را تعیین میکند. شیء فرزند اندازه خود را در چارچوب این محدودیتها محاسبه کرده و به والد برمیگرداند. این فرآیند "downward pass" (انتقال محدودیتها به پایین) و "upward pass" (انتقال اندازهها به بالا) نامیده میشود.
پس از اتمام layout، Flutter paint — رسم هر RenderObject را انجام میدهد. والد یک PaintingContext ایجاد کرده، آن را به اشیاء فرزند منتقل کرده و offset را مشخص میکند. هر RenderObject خود را روی Canvas با استفاده از ابتداییهای گرافیکی رسم میکند: مستطیلها، دایرهها، متن، تصاویر و تبدیلها. Canvas مستقیماً با Skia یا Impeller تعامل دارد.
Flutter از مکانیزم گرههای "کثیف" برای به حداقل رساندن کار استفاده میکند. وقتی فقط اندازه یک RenderObject تغییر میکند، Flutter layout کل درخت را دوباره محاسبه نمیکند — گره تغییرکرده و اجداد احتمالی آن را تا نزدیکترین RenderBox با اندازه ثابت علامتگذاری میکند. به طور مشابه، با تغییر ظاهر، فقط paint بدون تکرار layout علامتگذاری میشود.
Flutter ارائه میدهد چندین دسته RenderObject برای وظایف مختلف: RenderBox برای عناصر مستطیلی استاندارد، RenderSliver برای نواحی اسکرولشدنی و RenderObject سفارشی برای رندرینگ غیراستاندارد از طریق CustomPainter. هر نوع برای نقش خود در درخت رندرینگ بهینهسازی شده است.
RenderBox — کلاس پایه برای عناصر مستطیلی رابط. زیرنوعهای آن: RenderPadding (افزودن فاصله), RenderTransform (اعمال تبدیلها), RenderFlex (پیادهسازی Row و Column), RenderStack (قرارگیری عناصر روی هم), RenderImage (نمایش تصاویر), RenderParagraph (نمایش متن). هر زیرنوع performLayout و paint را برای منطق خود بازنویسی میکند.
RenderSliver — نوعی RenderObject برای کار با نواحی اسکرولشدنی است. برخلاف RenderBox، Sliver از SliverConstraints استفاده میکند که شامل اطلاعاتی درباره ناحیه قابل مشاهده (viewport) است. RenderSliverList و RenderSliverGrid فقط برای عناصری که در ناحیه قابل مشاهده قرار میگیرند RenderObject ایجاد میکنند که امکان پردازش لیستهایی با میلیونها رکورد را فراهم میکند.
برای گرافیک غیراستاندارد از CustomPaint و CustomPainter استفاده کنید. CustomPaint یک RenderCustomPaint ایجاد میکند که متدهای paint را در CustomPainter فراخوانی میکند. این امکان رسم اشکال دلخواه، نمودارها و انیمیشنها با کنترل کامل روی Canvas را فراهم میکند. به گفته Flutter Team، CustomPainter برای گرافیک برداری پیچیده از ویجتهای استاندارد تو در تو کارآمدتر است.
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 Tree از Element Tree از طریق مکانیزم RenderObjectWidget ایجاد میشود. هر RenderObjectWidget (Padding, Transform, CustomPaint) یک RenderObjectElement ایجاد میکند که RenderObject مربوطه را ایجاد و مدیریت میکند. عنصر نقش واسطه را ایفا میکند: پیکربندی را از ویجت به RenderObject منتقل کرده و RenderObject را از تغییرات مطلع میکند.
وقتی RenderObjectElement نصب میشود، متد createRenderObject ویجت خود را فراخوانی میکند. ویجت یک نمونه RenderObject ایجاد کرده و به عنصر برمیگرداند. عنصر با فراخوانی متد insertChildLayout در RenderObject والد، RenderObject را در RenderObject Tree جاسازی میکند. این فرآیند فقط در اولین نصب رخ میدهد — در بهروزرسانیهای بعدی، عنصر فقط پارامترهای RenderObject موجود را بهروزرسانی میکند.
وقتی پیکربندی ویجت تغییر میکند (مثلاً مقدار padding تغییر میکند)، عنصر متد updateRenderObject را فراخوانی میکند که پیکربندی جدید را به RenderObject موجود منتقل میکند. RenderObject خود را برای layout یا paint به عنوان "کثیف" علامتگذاری میکند و در فریم بعدی فریمورک بهروزرسانیهای لازم را انجام میدهد.
هنگام جدا کردن عنصر، متد unmount فراخوانی میشود که RenderObject را از RenderObject Tree حذف کرده و منابع را آزاد میکند. RenderObject.remove برای جدا کردن از والد فراخوانی میشود، پس از آن شیء میتواند توسط جمعآوری زباله جمعآوری شود. Flutter تضمین میکند که RenderObject بدون عنصر مربوطه در درخت باقی نمیماند.
RepaintBoundary — ویجتی است که یک لایه جداگانه برای رندرینگ محتوای خود ایجاد میکند. وقتی محتوای RepaintBoundary تغییر میکند، فقط این لایه بازرسم میشود و بقیه قسمتهای صفحه بدون تغییر میمانند. RepaintBoundary به ویژه برای انیمیشنها، پخشکنندههای ویدئو، نمودارهای تعاملی و سایر عناصر مکرراً بهروزرسانیشونده مفید است.
در هسته RepaintBoundary، RenderRepaintBoundary — یک RenderObject ویژه قرار دارد که یک PictureLayer جداگانه ایجاد میکند. در اولین رندرینگ، RenderRepaintBoundary دستورات گرافیکی را در این لایه ضبط میکند. در بهروزرسانیهای بعدی، اگر فقط محتوای داخل RepaintBoundary تغییر کرده باشد، Flutter فقط این لایه را بازرسم میکند، نه کل صفحه را. بقیه لایهها بدون تغییر باقی مانده و دوباره استفاده میشوند.
هر ویجتی به RepaintBoundary نیاز ندارد. وقتی بخشی از رابط با فرکانس بالا (۶۰ FPS و بالاتر) بهروزرسانی میشود و بقیه صفحه ثابت است، از آن استفاده کنید. مثالهای معمول: نشانگر بارگذاری متحرک، پخشکننده ویدئو، Canvas بازی، CustomPainter با بازرسم مکرر. برای متن ثابت یا دکمهها، RepaintBoundary اضافی است و فقط مصرف حافظه را افزایش میدهد.
| سناریو | توصیه | دلیل |
|---|---|---|
| انیمیشن | در RepaintBoundary بپیچید | ناحیه مکرراً بهروزرسانیشونده را جدا میکند |
| لیست اسکرولشدنی | نیاز نیست | ListView به طور خودکار از لایهها استفاده میکند |
| متن ثابت | نیاز نیست | بازرسم مکرر وجود ندارد |
| CustomPainter | توصیه میشود | بازرسم مکرر گرافیک |
عملکرد RenderObject Tree به تعداد گرهها، عمق درخت و دفعات بازرسم بستگی دارد. ابزارهای Flutter DevTools (برگه "Rendering") امکان تحلیل RenderObject Tree را در زمان واقعی فراهم میکنند: تعداد repaint، زمان layout و paint، تعداد لایهها و اندازههای آنها. تحلیل منظم به شناسایی تنگناها کمک میکند.
Overdraw — وضعیتی است که یک پیکسل چندین بار در هر فریم رسم میشود. مثلاً وقتی یک ویجت نیمهشفاف روی ویجت دیگر قرار میگیرد، GPU هر دو لایه را رسم میکند. از پرچمهای opaque (Container با color به جای decoration) برای عناصر غیرشفاف استفاده کنید تا Flutter لایههای نامرئی را نادیده بگیرد. به گفته Flutter Team، کاهش overdraw میتواند زمان paint را تا ۳۰٪ کاهش دهد.
هر RepaintBoundary و برخی ویجتها (Opacity, ClipRRect, Transform) یک لایه جداگانه (PictureLayer) ایجاد میکنند. لایههای زیاد زمان ترکیب را افزایش میدهند. از گروهبندی استفاده کنید: به جای چند Opacity روی ویجتهای جداگانه، یک Opacity روی کانتینر اعمال کنید. به جای ClipRRect روی هر عنصر، از ClipRRect روی کانتینر مشترک استفاده کنید.
وقتی ویجت به عنوان const اعلام میشود، Flutter میداند که پیکربندی آن تغییر نخواهد کرد و میتواند RenderObject مربوطه را بدون بازسازی دوباره استفاده کند. سازندههای const بار جمعآوری زباله را کاهش داده و اولین فریم را سرعت میبخشند. برای ویجتهایی با پارامترهای ثابت از const استفاده کنید: آیکونها، عنوانها، عناصر تزئینی.
const Text("عنوان", style: TextStyle(fontSize: 24));
const Icon(Icons.home, color: Colors.blue);
const SizedBox.shrink();
سوالات متداول
Widget Tree — پیکربندی سبک رابط است که در هر rebuild بازسازی میشود. RenderObject Tree — درخت سنگینی از اشیاء رندرینگ است که حفظ شده و فقط با تغییر هندسه یا ظاهر بهروزرسانی میشود.
از Flutter DevTools — برگه "Rendering" استفاده کنید. لایههای رندرینگ، زمان layout و paint و همچنین اطلاعات دقیق درباره هر RenderObject را مشاهده خواهید کرد: اندازهها، constraints، پرچمهای نیاز به بهروزرسانی.
Dirty — RenderObjectی است که به عنوان نیازمند بهروزرسانی علامتگذاری شده است. پرچم _needsLayout یا _needsPaint با تغییر پیکربندی تنظیم میشود و Flutter در فریم بعدی layout یا paint را برای این گره انجام میدهد.
بله، یک زیرکلاس از RenderBox ایجاد کرده و متدهای performLayout و paint را بازنویسی کنید. از RenderObjectWidget برای جاسازی RenderObject سفارشی در Widget Tree استفاده کنید. این یک تکنیک پیشرفته برای رندرینگ غیراستاندارد است.
RepaintBoundary یک RenderRepaintBoundary ایجاد میکند که بخشی از RenderObject Tree را در یک لایه جداگانه ایزوله میکند. با تغییر محتوا، فقط این لایه بازرسم میشود و بقیه درخت بدون تغییر میماند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید