handleBeginFrame method

void handleBeginFrame(
  1. Duration? rawTimeStamp
)

Called by the engine to prepare the framework to produce a new frame.

This function calls all the transient frame callbacks registered by scheduleFrameCallback. It then returns, any scheduled microtasks are run (e.g. handlers for any Futures resolved by transient frame callbacks), and handleDrawFrame is called to continue the frame.

If the given time stamp is null, the time stamp from the last frame is reused.

To have a banner shown at the start of every frame in debug mode, set debugPrintBeginFrameBanner to true. The banner will be printed to the console using debugPrint and will contain the frame number (which increments by one for each frame), and the time stamp of the frame. If the given time stamp was null, then the string "warm-up frame" is shown instead of the time stamp. This allows frames eagerly pushed by the framework to be distinguished from those requested by the engine in response to the "Vsync" signal from the operating system.

You can also show a banner at the end of every frame by setting debugPrintEndFrameBanner to true. This allows you to distinguish log statements printed during a frame from those printed between frames (e.g. in response to events or timers).

Implementation

void handleBeginFrame(Duration? rawTimeStamp) {
  _frameTimelineTask?.start('Frame');
  _firstRawTimeStampInEpoch ??= rawTimeStamp;
  _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
  if (rawTimeStamp != null) {
    _lastRawTimeStamp = rawTimeStamp;
  }

  assert(() {
    _debugFrameNumber += 1;

    if (debugPrintBeginFrameBanner || debugPrintEndFrameBanner) {
      final StringBuffer frameTimeStampDescription = StringBuffer();
      if (rawTimeStamp != null) {
        _debugDescribeTimeStamp(_currentFrameTimeStamp!, frameTimeStampDescription);
      } else {
        frameTimeStampDescription.write('(warm-up frame)');
      }
      _debugBanner = '▄▄▄▄▄▄▄▄ Frame ${_debugFrameNumber.toString().padRight(7)}   ${frameTimeStampDescription.toString().padLeft(18)} ▄▄▄▄▄▄▄▄';
      if (debugPrintBeginFrameBanner) {
        debugPrint(_debugBanner);
      }
    }
    return true;
  }());

  assert(schedulerPhase == SchedulerPhase.idle);
  _hasScheduledFrame = false;
  try {
    // TRANSIENT FRAME CALLBACKS
    _frameTimelineTask?.start('Animate');
    _schedulerPhase = SchedulerPhase.transientCallbacks;
    final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
    _transientCallbacks = <int, _FrameCallbackEntry>{};
    callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
      if (!_removedIds.contains(id)) {
        _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
      }
    });
    _removedIds.clear();
  } finally {
    _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
  }
}