الإجابة المختصرة
يمر كل فريم بالتسلسل: Transient Callbacks (الأنيميشن) -> Microtasks -> Persistent Callbacks التي تشمل Build (بناء الودجت) ثم Layout (حساب القياسات) ثم Compositing Bits ثم Paint (تسجيل أوامر الرسم) ثم Composite (إرسال المشهد للـ GPU) ثم Semantics -> وأخيراً Post-frame Callbacks.
الشرح
يدير PipelineOwner شجرة الرندر وينظم مراحلها بكفاءة لتجنب تكرار العمل. تبدأ الدورة بطلب Vsync من الشاشة. يُشغل المحرك أحداث الأنيميشن عبر Tickers، ثم تُنفذ دالة BuildOwner.buildScope() لمعالجة العناصر المتسخة (Dirty Elements). بعد استقرار الشجرة، يتولى PipelineOwner.flushLayout() حساب أبعاد العناصر التي طُلب لها markNeedsLayout()، يليه flushCompositingBits()، ثم flushPaint() لإعادة تسجيل أوامر الرسم للـ RenderObjects المتسخة، وأخيراً ترسل طبقات المشهد SceneBuilder للـ GPU عبر Window.render().
مثال بالكود
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
void observeOneFrame() {
final binding = WidgetsBinding.instance;
binding.scheduleFrameCallback((_) => debugPrint('1. transient callback (tickers / animations)'));
// 2. microtasks run, 3. persistent callbacks run drawFrame():
// build → layout → compositing bits → paint → composite → semantics
binding.addPostFrameCallback((_) => debugPrint('4. post-frame callback (layout is final)'));
binding.addTimingsCallback((timings) {
for (final t in timings) {
debugPrint('build ${t.buildDuration.inMicroseconds}µs, raster ${t.rasterDuration.inMicroseconds}µs');
}
});
binding.scheduleFrame(); // ask the engine for a new frame
}أخطاء شائعة
- استدعاء setState() أثناء مرحلة الـ build أو الـ layout مما يؤدي لرمي استثناء 'setState() or markNeedsBuild() called during build'.