"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter/lib/src/scheduler/binding.dart" (13 Nov 2020, 44978 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 import 'dart:async';
    6 import 'dart:collection';
    7 import 'dart:developer' show Flow, Timeline;
    8 import 'dart:ui' show AppLifecycleState, FramePhase, FrameTiming, TimingsCallback;
    9 
   10 import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
   11 import 'package:flutter/foundation.dart';
   12 
   13 import 'debug.dart';
   14 import 'priority.dart';
   15 
   16 export 'dart:ui' show AppLifecycleState, VoidCallback, FrameTiming;
   17 
   18 /// Slows down animations by this factor to help in development.
   19 double get timeDilation => _timeDilation;
   20 double _timeDilation = 1.0;
   21 /// Setting the time dilation automatically calls [SchedulerBinding.resetEpoch]
   22 /// to ensure that time stamps seen by consumers of the scheduler binding are
   23 /// always increasing.
   24 set timeDilation(double value) {
   25   assert(value > 0.0);
   26   if (_timeDilation == value)
   27     return;
   28   // We need to resetEpoch first so that we capture start of the epoch with the
   29   // current time dilation.
   30   SchedulerBinding.instance?.resetEpoch();
   31   _timeDilation = value;
   32 }
   33 
   34 /// Signature for frame-related callbacks from the scheduler.
   35 ///
   36 /// The `timeStamp` is the number of milliseconds since the beginning of the
   37 /// scheduler's epoch. Use timeStamp to determine how far to advance animation
   38 /// timelines so that all the animations in the system are synchronized to a
   39 /// common time base.
   40 typedef FrameCallback = void Function(Duration timeStamp);
   41 
   42 /// Signature for [SchedulerBinding.scheduleTask] callbacks.
   43 ///
   44 /// The type argument `T` is the task's return value. Consider `void` if the
   45 /// task does not return a value.
   46 typedef TaskCallback<T> = T Function();
   47 
   48 /// Signature for the [SchedulerBinding.schedulingStrategy] callback. Called
   49 /// whenever the system needs to decide whether a task at a given
   50 /// priority needs to be run.
   51 ///
   52 /// Return true if a task with the given priority should be executed at this
   53 /// time, false otherwise.
   54 ///
   55 /// See also:
   56 ///
   57 ///  * [defaultSchedulingStrategy], the default [SchedulingStrategy] for [SchedulerBinding.schedulingStrategy].
   58 typedef SchedulingStrategy = bool Function({ required int priority, required SchedulerBinding scheduler });
   59 
   60 class _TaskEntry<T> {
   61   _TaskEntry(this.task, this.priority, this.debugLabel, this.flow) {
   62     assert(() {
   63       debugStack = StackTrace.current;
   64       return true;
   65     }());
   66   }
   67   final TaskCallback<T> task;
   68   final int priority;
   69   final String? debugLabel;
   70   final Flow? flow;
   71 
   72   late StackTrace debugStack;
   73   final Completer<T> completer = Completer<T>();
   74 
   75   void run() {
   76     if (!kReleaseMode) {
   77       Timeline.timeSync(
   78         debugLabel ?? 'Scheduled Task',
   79         () {
   80           completer.complete(task());
   81         },
   82         flow: flow != null ? Flow.step(flow!.id) : null,
   83       );
   84     } else {
   85       completer.complete(task());
   86     }
   87   }
   88 }
   89 
   90 class _FrameCallbackEntry {
   91   _FrameCallbackEntry(this.callback, { bool rescheduling = false }) {
   92     assert(() {
   93       if (rescheduling) {
   94         assert(() {
   95           if (debugCurrentCallbackStack == null) {
   96             throw FlutterError.fromParts(<DiagnosticsNode>[
   97               ErrorSummary('scheduleFrameCallback called with rescheduling true, but no callback is in scope.'),
   98               ErrorDescription(
   99                 'The "rescheduling" argument should only be set to true if the '
  100                 'callback is being reregistered from within the callback itself, '
  101                 'and only then if the callback itself is entirely synchronous.'
  102               ),
  103               ErrorHint(
  104                 'If this is the initial registration of the callback, or if the '
  105                 'callback is asynchronous, then do not use the "rescheduling" '
  106                 'argument.'
  107               )
  108             ]);
  109           }
  110           return true;
  111         }());
  112         debugStack = debugCurrentCallbackStack;
  113       } else {
  114         // TODO(ianh): trim the frames from this library, so that the call to scheduleFrameCallback is the top one
  115         debugStack = StackTrace.current;
  116       }
  117       return true;
  118     }());
  119   }
  120 
  121   final FrameCallback callback;
  122 
  123   static StackTrace? debugCurrentCallbackStack;
  124   StackTrace? debugStack;
  125 }
  126 
  127 /// The various phases that a [SchedulerBinding] goes through during
  128 /// [SchedulerBinding.handleBeginFrame].
  129 ///
  130 /// This is exposed by [SchedulerBinding.schedulerPhase].
  131 ///
  132 /// The values of this enum are ordered in the same order as the phases occur,
  133 /// so their relative index values can be compared to each other.
  134 ///
  135 /// See also:
  136 ///
  137 ///  * [WidgetsBinding.drawFrame], which pumps the build and rendering pipeline
  138 ///    to generate a frame.
  139 enum SchedulerPhase {
  140   /// No frame is being processed. Tasks (scheduled by
  141   /// [SchedulerBinding.scheduleTask]), microtasks (scheduled by
  142   /// [scheduleMicrotask]), [Timer] callbacks, event handlers (e.g. from user
  143   /// input), and other callbacks (e.g. from [Future]s, [Stream]s, and the like)
  144   /// may be executing.
  145   idle,
  146 
  147   /// The transient callbacks (scheduled by
  148   /// [SchedulerBinding.scheduleFrameCallback]) are currently executing.
  149   ///
  150   /// Typically, these callbacks handle updating objects to new animation
  151   /// states.
  152   ///
  153   /// See [SchedulerBinding.handleBeginFrame].
  154   transientCallbacks,
  155 
  156   /// Microtasks scheduled during the processing of transient callbacks are
  157   /// current executing.
  158   ///
  159   /// This may include, for instance, callbacks from futures resolved during the
  160   /// [transientCallbacks] phase.
  161   midFrameMicrotasks,
  162 
  163   /// The persistent callbacks (scheduled by
  164   /// [SchedulerBinding.addPersistentFrameCallback]) are currently executing.
  165   ///
  166   /// Typically, this is the build/layout/paint pipeline. See
  167   /// [WidgetsBinding.drawFrame] and [SchedulerBinding.handleDrawFrame].
  168   persistentCallbacks,
  169 
  170   /// The post-frame callbacks (scheduled by
  171   /// [SchedulerBinding.addPostFrameCallback]) are currently executing.
  172   ///
  173   /// Typically, these callbacks handle cleanup and scheduling of work for the
  174   /// next frame.
  175   ///
  176   /// See [SchedulerBinding.handleDrawFrame].
  177   postFrameCallbacks,
  178 }
  179 
  180 /// Scheduler for running the following:
  181 ///
  182 /// * _Transient callbacks_, triggered by the system's [Window.onBeginFrame]
  183 ///   callback, for synchronizing the application's behavior to the system's
  184 ///   display. For example, [Ticker]s and [AnimationController]s trigger from
  185 ///   these.
  186 ///
  187 /// * _Persistent callbacks_, triggered by the system's [Window.onDrawFrame]
  188 ///   callback, for updating the system's display after transient callbacks have
  189 ///   executed. For example, the rendering layer uses this to drive its
  190 ///   rendering pipeline.
  191 ///
  192 /// * _Post-frame callbacks_, which are run after persistent callbacks, just
  193 ///   before returning from the [Window.onDrawFrame] callback.
  194 ///
  195 /// * Non-rendering tasks, to be run between frames. These are given a
  196 ///   priority and are executed in priority order according to a
  197 ///   [schedulingStrategy].
  198 mixin SchedulerBinding on BindingBase {
  199   @override
  200   void initInstances() {
  201     super.initInstances();
  202     _instance = this;
  203 
  204     if (!kReleaseMode) {
  205       int frameNumber = 0;
  206       addTimingsCallback((List<FrameTiming> timings) {
  207         for (final FrameTiming frameTiming in timings) {
  208           frameNumber += 1;
  209           _profileFramePostEvent(frameNumber, frameTiming);
  210         }
  211       });
  212     }
  213   }
  214 
  215   final List<TimingsCallback> _timingsCallbacks = <TimingsCallback>[];
  216 
  217   /// Add a [TimingsCallback] that receives [FrameTiming] sent from
  218   /// the engine.
  219   ///
  220   /// This API enables applications to monitor their graphics
  221   /// performance. Data from the engine is batched into lists of
  222   /// [FrameTiming] objects which are reported approximately once a
  223   /// second in release mode and approximately once every 100ms in
  224   /// debug and profile builds. The list is sorted in ascending
  225   /// chronological order (earliest frame first). The timing of the
  226   /// first frame is sent immediately without batching.
  227   ///
  228   /// The data returned can be used to catch missed frames (by seeing
  229   /// if [FrameTiming.buildDuration] or [FrameTiming.rasterDuration]
  230   /// exceed the frame budget, e.g. 16ms at 60Hz), and to catch high
  231   /// latency (by seeing if [FrameTiming.totalSpan] exceeds the frame
  232   /// budget). It is possible for no frames to be missed but for the
  233   /// latency to be more than one frame in the case where the Flutter
  234   /// engine is pipelining the graphics updates, e.g. because the sum
  235   /// of the [FrameTiming.buildDuration] and the
  236   /// [FrameTiming.rasterDuration] together exceed the frame budget.
  237   /// In those cases, animations will be smooth but touch input will
  238   /// feel more sluggish.
  239   ///
  240   /// Using [addTimingsCallback] is preferred over using
  241   /// [Window.onReportTimings] directly because the
  242   /// [Window.onReportTimings] API only allows one callback, which
  243   /// prevents multiple libraries from registering listeners
  244   /// simultaneously, while this API allows multiple callbacks to be
  245   /// registered independently.
  246   ///
  247   /// This API is implemented in terms of [Window.onReportTimings]. In
  248   /// release builds, when no libraries have registered with this API,
  249   /// the [Window.onReportTimings] callback is not set, which disables
  250   /// the performance tracking and reduces the runtime overhead to
  251   /// approximately zero. The performance overhead of the performance
  252   /// tracking when one or more callbacks are registered (i.e. when it
  253   /// is enabled) is very approximately 0.01% CPU usage per second
  254   /// (measured on an iPhone 6s).
  255   ///
  256   /// In debug and profile builds, the [SchedulerBinding] itself
  257   /// registers a timings callback to update the [Timeline].
  258   ///
  259   /// If the same callback is added twice, it will be executed twice.
  260   ///
  261   /// See also:
  262   ///
  263   ///  * [removeTimingsCallback], which can be used to remove a callback
  264   ///    added using this method.
  265   void addTimingsCallback(TimingsCallback callback) {
  266     _timingsCallbacks.add(callback);
  267     if (_timingsCallbacks.length == 1) {
  268       assert(window.onReportTimings == null);
  269       window.onReportTimings = _executeTimingsCallbacks;
  270     }
  271     assert(window.onReportTimings == _executeTimingsCallbacks);
  272   }
  273 
  274   /// Removes a callback that was earlier added by [addTimingsCallback].
  275   void removeTimingsCallback(TimingsCallback callback) {
  276     assert(_timingsCallbacks.contains(callback));
  277     _timingsCallbacks.remove(callback);
  278     if (_timingsCallbacks.isEmpty) {
  279       window.onReportTimings = null;
  280     }
  281   }
  282 
  283   void _executeTimingsCallbacks(List<FrameTiming> timings) {
  284     final List<TimingsCallback> clonedCallbacks =
  285         List<TimingsCallback>.from(_timingsCallbacks);
  286     for (final TimingsCallback callback in clonedCallbacks) {
  287       try {
  288         if (_timingsCallbacks.contains(callback)) {
  289           callback(timings);
  290         }
  291       } catch (exception, stack) {
  292         InformationCollector? collector;
  293         assert(() {
  294           collector = () sync* {
  295             yield DiagnosticsProperty<TimingsCallback>(
  296               'The TimingsCallback that gets executed was',
  297               callback,
  298               style: DiagnosticsTreeStyle.errorProperty,
  299             );
  300           };
  301           return true;
  302         }());
  303         FlutterError.reportError(FlutterErrorDetails(
  304           exception: exception,
  305           stack: stack,
  306           context: ErrorDescription('while executing callbacks for FrameTiming'),
  307           informationCollector: collector
  308         ));
  309       }
  310     }
  311   }
  312 
  313   /// The current [SchedulerBinding], if one has been created.
  314   static SchedulerBinding? get instance => _instance;
  315   static SchedulerBinding? _instance;
  316 
  317   @override
  318   void initServiceExtensions() {
  319     super.initServiceExtensions();
  320 
  321     if (!kReleaseMode) {
  322       registerNumericServiceExtension(
  323         name: 'timeDilation',
  324         getter: () async => timeDilation,
  325         setter: (double value) async {
  326           timeDilation = value;
  327         },
  328       );
  329     }
  330   }
  331 
  332   /// Whether the application is visible, and if so, whether it is currently
  333   /// interactive.
  334   ///
  335   /// This is set by [handleAppLifecycleStateChanged] when the
  336   /// [SystemChannels.lifecycle] notification is dispatched.
  337   ///
  338   /// The preferred way to watch for changes to this value is using
  339   /// [WidgetsBindingObserver.didChangeAppLifecycleState].
  340   AppLifecycleState? get lifecycleState => _lifecycleState;
  341   AppLifecycleState? _lifecycleState;
  342 
  343   /// Called when the application lifecycle state changes.
  344   ///
  345   /// Notifies all the observers using
  346   /// [WidgetsBindingObserver.didChangeAppLifecycleState].
  347   ///
  348   /// This method exposes notifications from [SystemChannels.lifecycle].
  349   @protected
  350   @mustCallSuper
  351   void handleAppLifecycleStateChanged(AppLifecycleState state) {
  352     assert(state != null);
  353     _lifecycleState = state;
  354     switch (state) {
  355       case AppLifecycleState.resumed:
  356       case AppLifecycleState.inactive:
  357         _setFramesEnabledState(true);
  358         break;
  359       case AppLifecycleState.paused:
  360       case AppLifecycleState.detached:
  361         _setFramesEnabledState(false);
  362         break;
  363     }
  364   }
  365 
  366   /// The strategy to use when deciding whether to run a task or not.
  367   ///
  368   /// Defaults to [defaultSchedulingStrategy].
  369   SchedulingStrategy schedulingStrategy = defaultSchedulingStrategy;
  370 
  371   static int _taskSorter (_TaskEntry<dynamic> e1, _TaskEntry<dynamic> e2) {
  372     return -e1.priority.compareTo(e2.priority);
  373   }
  374   final PriorityQueue<_TaskEntry<dynamic>> _taskQueue = HeapPriorityQueue<_TaskEntry<dynamic>>(_taskSorter);
  375 
  376   /// Schedules the given `task` with the given `priority` and returns a
  377   /// [Future] that completes to the `task`'s eventual return value.
  378   ///
  379   /// The `debugLabel` and `flow` are used to report the task to the [Timeline],
  380   /// for use when profiling.
  381   ///
  382   /// ## Processing model
  383   ///
  384   /// Tasks will be executed between frames, in priority order,
  385   /// excluding tasks that are skipped by the current
  386   /// [schedulingStrategy]. Tasks should be short (as in, up to a
  387   /// millisecond), so as to not cause the regular frame callbacks to
  388   /// get delayed.
  389   ///
  390   /// If an animation is running, including, for instance, a [ProgressIndicator]
  391   /// indicating that there are pending tasks, then tasks with a priority below
  392   /// [Priority.animation] won't run (at least, not with the
  393   /// [defaultSchedulingStrategy]; this can be configured using
  394   /// [schedulingStrategy]).
  395   Future<T> scheduleTask<T>(
  396     TaskCallback<T> task,
  397     Priority priority, {
  398     String? debugLabel,
  399     Flow? flow,
  400   }) {
  401     final bool isFirstTask = _taskQueue.isEmpty;
  402     final _TaskEntry<T> entry = _TaskEntry<T>(
  403       task,
  404       priority.value,
  405       debugLabel,
  406       flow,
  407     );
  408     _taskQueue.add(entry);
  409     if (isFirstTask && !locked)
  410       _ensureEventLoopCallback();
  411     return entry.completer.future;
  412   }
  413 
  414   @override
  415   void unlocked() {
  416     super.unlocked();
  417     if (_taskQueue.isNotEmpty)
  418       _ensureEventLoopCallback();
  419   }
  420 
  421   // Whether this scheduler already requested to be called from the event loop.
  422   bool _hasRequestedAnEventLoopCallback = false;
  423 
  424   // Ensures that the scheduler services a task scheduled by
  425   // [SchedulerBinding.scheduleTask].
  426   void _ensureEventLoopCallback() {
  427     assert(!locked);
  428     assert(_taskQueue.isNotEmpty);
  429     if (_hasRequestedAnEventLoopCallback)
  430       return;
  431     _hasRequestedAnEventLoopCallback = true;
  432     Timer.run(_runTasks);
  433   }
  434 
  435   // Scheduled by _ensureEventLoopCallback.
  436   void _runTasks() {
  437     _hasRequestedAnEventLoopCallback = false;
  438     if (handleEventLoopCallback())
  439       _ensureEventLoopCallback(); // runs next task when there's time
  440   }
  441 
  442   /// Execute the highest-priority task, if it is of a high enough priority.
  443   ///
  444   /// Returns true if a task was executed and there are other tasks remaining
  445   /// (even if they are not high-enough priority).
  446   ///
  447   /// Returns false if no task was executed, which can occur if there are no
  448   /// tasks scheduled, if the scheduler is [locked], or if the highest-priority
  449   /// task is of too low a priority given the current [schedulingStrategy].
  450   ///
  451   /// Also returns false if there are no tasks remaining.
  452   @visibleForTesting
  453   bool handleEventLoopCallback() {
  454     if (_taskQueue.isEmpty || locked)
  455       return false;
  456     final _TaskEntry<dynamic> entry = _taskQueue.first;
  457     if (schedulingStrategy(priority: entry.priority, scheduler: this)) {
  458       try {
  459         _taskQueue.removeFirst();
  460         entry.run();
  461       } catch (exception, exceptionStack) {
  462         StackTrace? callbackStack;
  463         assert(() {
  464           callbackStack = entry.debugStack;
  465           return true;
  466         }());
  467         FlutterError.reportError(FlutterErrorDetails(
  468           exception: exception,
  469           stack: exceptionStack,
  470           library: 'scheduler library',
  471           context: ErrorDescription('during a task callback'),
  472           informationCollector: (callbackStack == null) ? null : () sync* {
  473             yield DiagnosticsStackTrace(
  474               '\nThis exception was thrown in the context of a scheduler callback. '
  475               'When the scheduler callback was _registered_ (as opposed to when the '
  476               'exception was thrown), this was the stack',
  477               callbackStack,
  478             );
  479           },
  480         ));
  481       }
  482       return _taskQueue.isNotEmpty;
  483     }
  484     return false;
  485   }
  486 
  487   int _nextFrameCallbackId = 0; // positive
  488   Map<int, _FrameCallbackEntry> _transientCallbacks = <int, _FrameCallbackEntry>{};
  489   final Set<int> _removedIds = HashSet<int>();
  490 
  491   /// The current number of transient frame callbacks scheduled.
  492   ///
  493   /// This is reset to zero just before all the currently scheduled
  494   /// transient callbacks are called, at the start of a frame.
  495   ///
  496   /// This number is primarily exposed so that tests can verify that
  497   /// there are no unexpected transient callbacks still registered
  498   /// after a test's resources have been gracefully disposed.
  499   int get transientCallbackCount => _transientCallbacks.length;
  500 
  501   /// Schedules the given transient frame callback.
  502   ///
  503   /// Adds the given callback to the list of frame callbacks and ensures that a
  504   /// frame is scheduled.
  505   ///
  506   /// If this is a one-off registration, ignore the `rescheduling` argument.
  507   ///
  508   /// If this is a callback that will be re-registered each time it fires, then
  509   /// when you re-register the callback, set the `rescheduling` argument to
  510   /// true. This has no effect in release builds, but in debug builds, it
  511   /// ensures that the stack trace that is stored for this callback is the
  512   /// original stack trace for when the callback was _first_ registered, rather
  513   /// than the stack trace for when the callback is re-registered. This makes it
  514   /// easier to track down the original reason that a particular callback was
  515   /// called. If `rescheduling` is true, the call must be in the context of a
  516   /// frame callback.
  517   ///
  518   /// Callbacks registered with this method can be canceled using
  519   /// [cancelFrameCallbackWithId].
  520   int scheduleFrameCallback(FrameCallback callback, { bool rescheduling = false }) {
  521     scheduleFrame();
  522     _nextFrameCallbackId += 1;
  523     _transientCallbacks[_nextFrameCallbackId] = _FrameCallbackEntry(callback, rescheduling: rescheduling);
  524     return _nextFrameCallbackId;
  525   }
  526 
  527   /// Cancels the transient frame callback with the given [id].
  528   ///
  529   /// Removes the given callback from the list of frame callbacks. If a frame
  530   /// has been requested, this does not also cancel that request.
  531   ///
  532   /// Transient frame callbacks are those registered using
  533   /// [scheduleFrameCallback].
  534   void cancelFrameCallbackWithId(int id) {
  535     assert(id > 0);
  536     _transientCallbacks.remove(id);
  537     _removedIds.add(id);
  538   }
  539 
  540   /// Asserts that there are no registered transient callbacks; if
  541   /// there are, prints their locations and throws an exception.
  542   ///
  543   /// A transient frame callback is one that was registered with
  544   /// [scheduleFrameCallback].
  545   ///
  546   /// This is expected to be called at the end of tests (the
  547   /// flutter_test framework does it automatically in normal cases).
  548   ///
  549   /// Call this method when you expect there to be no transient
  550   /// callbacks registered, in an assert statement with a message that
  551   /// you want printed when a transient callback is registered:
  552   ///
  553   /// ```dart
  554   /// assert(SchedulerBinding.instance.debugAssertNoTransientCallbacks(
  555   ///   'A leak of transient callbacks was detected while doing foo.'
  556   /// ));
  557   /// ```
  558   ///
  559   /// Does nothing if asserts are disabled. Always returns true.
  560   bool debugAssertNoTransientCallbacks(String reason) {
  561     assert(() {
  562       if (transientCallbackCount > 0) {
  563         // We cache the values so that we can produce them later
  564         // even if the information collector is called after
  565         // the problem has been resolved.
  566         final int count = transientCallbackCount;
  567         final Map<int, _FrameCallbackEntry> callbacks = Map<int, _FrameCallbackEntry>.from(_transientCallbacks);
  568         FlutterError.reportError(FlutterErrorDetails(
  569           exception: reason,
  570           library: 'scheduler library',
  571           informationCollector: () sync* {
  572             if (count == 1) {
  573               // TODO(jacobr): I have added an extra line break in this case.
  574               yield ErrorDescription(
  575                 'There was one transient callback left. '
  576                 'The stack trace for when it was registered is as follows:'
  577               );
  578             } else {
  579               yield ErrorDescription(
  580                 'There were $count transient callbacks left. '
  581                 'The stack traces for when they were registered are as follows:'
  582               );
  583             }
  584             for (final int id in callbacks.keys) {
  585               final _FrameCallbackEntry entry = callbacks[id]!;
  586               yield DiagnosticsStackTrace('── callback $id ──', entry.debugStack, showSeparator: false);
  587             }
  588           },
  589         ));
  590       }
  591       return true;
  592     }());
  593     return true;
  594   }
  595 
  596   /// Prints the stack for where the current transient callback was registered.
  597   ///
  598   /// A transient frame callback is one that was registered with
  599   /// [scheduleFrameCallback].
  600   ///
  601   /// When called in debug more and in the context of a transient callback, this
  602   /// function prints the stack trace from where the current transient callback
  603   /// was registered (i.e. where it first called [scheduleFrameCallback]).
  604   ///
  605   /// When called in debug mode in other contexts, it prints a message saying
  606   /// that this function was not called in the context a transient callback.
  607   ///
  608   /// In release mode, this function does nothing.
  609   ///
  610   /// To call this function, use the following code:
  611   ///
  612   /// ```dart
  613   /// SchedulerBinding.debugPrintTransientCallbackRegistrationStack();
  614   /// ```
  615   static void debugPrintTransientCallbackRegistrationStack() {
  616     assert(() {
  617       if (_FrameCallbackEntry.debugCurrentCallbackStack != null) {
  618         debugPrint('When the current transient callback was registered, this was the stack:');
  619         debugPrint(
  620           FlutterError.defaultStackFilter(
  621             FlutterError.demangleStackTrace(
  622               _FrameCallbackEntry.debugCurrentCallbackStack!,
  623             ).toString().trimRight().split('\n')
  624           ).join('\n')
  625         );
  626       } else {
  627         debugPrint('No transient callback is currently executing.');
  628       }
  629       return true;
  630     }());
  631   }
  632 
  633   final List<FrameCallback> _persistentCallbacks = <FrameCallback>[];
  634 
  635   /// Adds a persistent frame callback.
  636   ///
  637   /// Persistent callbacks are called after transient
  638   /// (non-persistent) frame callbacks.
  639   ///
  640   /// Does *not* request a new frame. Conceptually, persistent frame
  641   /// callbacks are observers of "begin frame" events. Since they are
  642   /// executed after the transient frame callbacks they can drive the
  643   /// rendering pipeline.
  644   ///
  645   /// Persistent frame callbacks cannot be unregistered. Once registered, they
  646   /// are called for every frame for the lifetime of the application.
  647   void addPersistentFrameCallback(FrameCallback callback) {
  648     _persistentCallbacks.add(callback);
  649   }
  650 
  651   final List<FrameCallback> _postFrameCallbacks = <FrameCallback>[];
  652 
  653   /// Schedule a callback for the end of this frame.
  654   ///
  655   /// Does *not* request a new frame.
  656   ///
  657   /// This callback is run during a frame, just after the persistent
  658   /// frame callbacks (which is when the main rendering pipeline has
  659   /// been flushed). If a frame is in progress and post-frame
  660   /// callbacks haven't been executed yet, then the registered
  661   /// callback is still executed during the frame. Otherwise, the
  662   /// registered callback is executed during the next frame.
  663   ///
  664   /// The callbacks are executed in the order in which they have been
  665   /// added.
  666   ///
  667   /// Post-frame callbacks cannot be unregistered. They are called exactly once.
  668   ///
  669   /// See also:
  670   ///
  671   ///  * [scheduleFrameCallback], which registers a callback for the start of
  672   ///    the next frame.
  673   void addPostFrameCallback(FrameCallback callback) {
  674     _postFrameCallbacks.add(callback);
  675   }
  676 
  677   Completer<void>? _nextFrameCompleter;
  678 
  679   /// Returns a Future that completes after the frame completes.
  680   ///
  681   /// If this is called between frames, a frame is immediately scheduled if
  682   /// necessary. If this is called during a frame, the Future completes after
  683   /// the current frame.
  684   ///
  685   /// If the device's screen is currently turned off, this may wait a very long
  686   /// time, since frames are not scheduled while the device's screen is turned
  687   /// off.
  688   Future<void> get endOfFrame {
  689     if (_nextFrameCompleter == null) {
  690       if (schedulerPhase == SchedulerPhase.idle)
  691         scheduleFrame();
  692       _nextFrameCompleter = Completer<void>();
  693       addPostFrameCallback((Duration timeStamp) {
  694         _nextFrameCompleter!.complete();
  695         _nextFrameCompleter = null;
  696       });
  697     }
  698     return _nextFrameCompleter!.future;
  699   }
  700 
  701   /// Whether this scheduler has requested that [handleBeginFrame] be called soon.
  702   bool get hasScheduledFrame => _hasScheduledFrame;
  703   bool _hasScheduledFrame = false;
  704 
  705   /// The phase that the scheduler is currently operating under.
  706   SchedulerPhase get schedulerPhase => _schedulerPhase;
  707   SchedulerPhase _schedulerPhase = SchedulerPhase.idle;
  708 
  709   /// Whether frames are currently being scheduled when [scheduleFrame] is called.
  710   ///
  711   /// This value depends on the value of the [lifecycleState].
  712   bool get framesEnabled => _framesEnabled;
  713 
  714   bool _framesEnabled = true;
  715   void _setFramesEnabledState(bool enabled) {
  716     if (_framesEnabled == enabled)
  717       return;
  718     _framesEnabled = enabled;
  719     if (enabled)
  720       scheduleFrame();
  721   }
  722 
  723   /// Ensures callbacks for `window.onBeginFrame` and `window.onDrawFrame`
  724   /// are registered.
  725   @protected
  726   void ensureFrameCallbacksRegistered() {
  727     window.onBeginFrame ??= _handleBeginFrame;
  728     window.onDrawFrame ??= _handleDrawFrame;
  729   }
  730 
  731   /// Schedules a new frame using [scheduleFrame] if this object is not
  732   /// currently producing a frame.
  733   ///
  734   /// Calling this method ensures that [handleDrawFrame] will eventually be
  735   /// called, unless it's already in progress.
  736   ///
  737   /// This has no effect if [schedulerPhase] is
  738   /// [SchedulerPhase.transientCallbacks] or [SchedulerPhase.midFrameMicrotasks]
  739   /// (because a frame is already being prepared in that case), or
  740   /// [SchedulerPhase.persistentCallbacks] (because a frame is actively being
  741   /// rendered in that case). It will schedule a frame if the [schedulerPhase]
  742   /// is [SchedulerPhase.idle] (in between frames) or
  743   /// [SchedulerPhase.postFrameCallbacks] (after a frame).
  744   void ensureVisualUpdate() {
  745     switch (schedulerPhase) {
  746       case SchedulerPhase.idle:
  747       case SchedulerPhase.postFrameCallbacks:
  748         scheduleFrame();
  749         return;
  750       case SchedulerPhase.transientCallbacks:
  751       case SchedulerPhase.midFrameMicrotasks:
  752       case SchedulerPhase.persistentCallbacks:
  753         return;
  754     }
  755   }
  756 
  757   /// If necessary, schedules a new frame by calling
  758   /// [Window.scheduleFrame].
  759   ///
  760   /// After this is called, the engine will (eventually) call
  761   /// [handleBeginFrame]. (This call might be delayed, e.g. if the device's
  762   /// screen is turned off it will typically be delayed until the screen is on
  763   /// and the application is visible.) Calling this during a frame forces
  764   /// another frame to be scheduled, even if the current frame has not yet
  765   /// completed.
  766   ///
  767   /// Scheduled frames are serviced when triggered by a "Vsync" signal provided
  768   /// by the operating system. The "Vsync" signal, or vertical synchronization
  769   /// signal, was historically related to the display refresh, at a time when
  770   /// hardware physically moved a beam of electrons vertically between updates
  771   /// of the display. The operation of contemporary hardware is somewhat more
  772   /// subtle and complicated, but the conceptual "Vsync" refresh signal continue
  773   /// to be used to indicate when applications should update their rendering.
  774   ///
  775   /// To have a stack trace printed to the console any time this function
  776   /// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
  777   ///
  778   /// See also:
  779   ///
  780   ///  * [scheduleForcedFrame], which ignores the [lifecycleState] when
  781   ///    scheduling a frame.
  782   ///  * [scheduleWarmUpFrame], which ignores the "Vsync" signal entirely and
  783   ///    triggers a frame immediately.
  784   void scheduleFrame() {
  785     if (_hasScheduledFrame || !framesEnabled)
  786       return;
  787     assert(() {
  788       if (debugPrintScheduleFrameStacks)
  789         debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
  790       return true;
  791     }());
  792     ensureFrameCallbacksRegistered();
  793     window.scheduleFrame();
  794     _hasScheduledFrame = true;
  795   }
  796 
  797   /// Schedules a new frame by calling [Window.scheduleFrame].
  798   ///
  799   /// After this is called, the engine will call [handleBeginFrame], even if
  800   /// frames would normally not be scheduled by [scheduleFrame] (e.g. even if
  801   /// the device's screen is turned off).
  802   ///
  803   /// The framework uses this to force a frame to be rendered at the correct
  804   /// size when the phone is rotated, so that a correctly-sized rendering is
  805   /// available when the screen is turned back on.
  806   ///
  807   /// To have a stack trace printed to the console any time this function
  808   /// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
  809   ///
  810   /// Prefer using [scheduleFrame] unless it is imperative that a frame be
  811   /// scheduled immediately, since using [scheduleForcedFrame] will cause
  812   /// significantly higher battery usage when the device should be idle.
  813   ///
  814   /// Consider using [scheduleWarmUpFrame] instead if the goal is to update the
  815   /// rendering as soon as possible (e.g. at application startup).
  816   void scheduleForcedFrame() {
  817     // TODO(chunhtai): Removes the if case once the issue is fixed
  818     // https://github.com/flutter/flutter/issues/45131
  819     if (!framesEnabled)
  820       return;
  821 
  822     if (_hasScheduledFrame)
  823       return;
  824     assert(() {
  825       if (debugPrintScheduleFrameStacks)
  826         debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.');
  827       return true;
  828     }());
  829     window.scheduleFrame();
  830     _hasScheduledFrame = true;
  831   }
  832 
  833   bool _warmUpFrame = false;
  834 
  835   /// Schedule a frame to run as soon as possible, rather than waiting for
  836   /// the engine to request a frame in response to a system "Vsync" signal.
  837   ///
  838   /// This is used during application startup so that the first frame (which is
  839   /// likely to be quite expensive) gets a few extra milliseconds to run.
  840   ///
  841   /// Locks events dispatching until the scheduled frame has completed.
  842   ///
  843   /// If a frame has already been scheduled with [scheduleFrame] or
  844   /// [scheduleForcedFrame], this call may delay that frame.
  845   ///
  846   /// If any scheduled frame has already begun or if another
  847   /// [scheduleWarmUpFrame] was already called, this call will be ignored.
  848   ///
  849   /// Prefer [scheduleFrame] to update the display in normal operation.
  850   void scheduleWarmUpFrame() {
  851     if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
  852       return;
  853 
  854     _warmUpFrame = true;
  855     Timeline.startSync('Warm-up frame');
  856     final bool hadScheduledFrame = _hasScheduledFrame;
  857     // We use timers here to ensure that microtasks flush in between.
  858     Timer.run(() {
  859       assert(_warmUpFrame);
  860       handleBeginFrame(null);
  861     });
  862     Timer.run(() {
  863       assert(_warmUpFrame);
  864       handleDrawFrame();
  865       // We call resetEpoch after this frame so that, in the hot reload case,
  866       // the very next frame pretends to have occurred immediately after this
  867       // warm-up frame. The warm-up frame's timestamp will typically be far in
  868       // the past (the time of the last real frame), so if we didn't reset the
  869       // epoch we would see a sudden jump from the old time in the warm-up frame
  870       // to the new time in the "real" frame. The biggest problem with this is
  871       // that implicit animations end up being triggered at the old time and
  872       // then skipping every frame and finishing in the new time.
  873       resetEpoch();
  874       _warmUpFrame = false;
  875       if (hadScheduledFrame)
  876         scheduleFrame();
  877     });
  878 
  879     // Lock events so touch events etc don't insert themselves until the
  880     // scheduled frame has finished.
  881     lockEvents(() async {
  882       await endOfFrame;
  883       Timeline.finishSync();
  884     });
  885   }
  886 
  887   Duration? _firstRawTimeStampInEpoch;
  888   Duration _epochStart = Duration.zero;
  889   Duration _lastRawTimeStamp = Duration.zero;
  890 
  891   /// Prepares the scheduler for a non-monotonic change to how time stamps are
  892   /// calculated.
  893   ///
  894   /// Callbacks received from the scheduler assume that their time stamps are
  895   /// monotonically increasing. The raw time stamp passed to [handleBeginFrame]
  896   /// is monotonic, but the scheduler might adjust those time stamps to provide
  897   /// [timeDilation]. Without careful handling, these adjusts could cause time
  898   /// to appear to run backwards.
  899   ///
  900   /// The [resetEpoch] function ensures that the time stamps are monotonic by
  901   /// resetting the base time stamp used for future time stamp adjustments to the
  902   /// current value. For example, if the [timeDilation] decreases, rather than
  903   /// scaling down the [Duration] since the beginning of time, [resetEpoch] will
  904   /// ensure that we only scale down the duration since [resetEpoch] was called.
  905   ///
  906   /// Setting [timeDilation] calls [resetEpoch] automatically. You don't need to
  907   /// call [resetEpoch] yourself.
  908   void resetEpoch() {
  909     _epochStart = _adjustForEpoch(_lastRawTimeStamp);
  910     _firstRawTimeStampInEpoch = null;
  911   }
  912 
  913   /// Adjusts the given time stamp into the current epoch.
  914   ///
  915   /// This both offsets the time stamp to account for when the epoch started
  916   /// (both in raw time and in the epoch's own time line) and scales the time
  917   /// stamp to reflect the time dilation in the current epoch.
  918   ///
  919   /// These mechanisms together combine to ensure that the durations we give
  920   /// during frame callbacks are monotonically increasing.
  921   Duration _adjustForEpoch(Duration rawTimeStamp) {
  922     final Duration rawDurationSinceEpoch = _firstRawTimeStampInEpoch == null ? Duration.zero : rawTimeStamp - _firstRawTimeStampInEpoch!;
  923     return Duration(microseconds: (rawDurationSinceEpoch.inMicroseconds / timeDilation).round() + _epochStart.inMicroseconds);
  924   }
  925 
  926   /// The time stamp for the frame currently being processed.
  927   ///
  928   /// This is only valid while between the start of [handleBeginFrame] and the
  929   /// end of the corresponding [handleDrawFrame], i.e. while a frame is being
  930   /// produced.
  931   Duration get currentFrameTimeStamp {
  932     assert(_currentFrameTimeStamp != null);
  933     return _currentFrameTimeStamp!;
  934   }
  935   Duration? _currentFrameTimeStamp;
  936 
  937   /// The raw time stamp as provided by the engine to [Window.onBeginFrame]
  938   /// for the frame currently being processed.
  939   ///
  940   /// Unlike [currentFrameTimeStamp], this time stamp is neither adjusted to
  941   /// offset when the epoch started nor scaled to reflect the [timeDilation] in
  942   /// the current epoch.
  943   ///
  944   /// On most platforms, this is a more or less arbitrary value, and should
  945   /// generally be ignored. On Fuchsia, this corresponds to the system-provided
  946   /// presentation time, and can be used to ensure that animations running in
  947   /// different processes are synchronized.
  948   Duration get currentSystemFrameTimeStamp {
  949     assert(_lastRawTimeStamp != null);
  950     return _lastRawTimeStamp;
  951   }
  952 
  953   int _debugFrameNumber = 0;
  954   String? _debugBanner;
  955   bool _ignoreNextEngineDrawFrame = false;
  956 
  957   void _handleBeginFrame(Duration rawTimeStamp) {
  958     if (_warmUpFrame) {
  959       assert(!_ignoreNextEngineDrawFrame);
  960       _ignoreNextEngineDrawFrame = true;
  961       return;
  962     }
  963     handleBeginFrame(rawTimeStamp);
  964   }
  965 
  966   void _handleDrawFrame() {
  967     if (_ignoreNextEngineDrawFrame) {
  968       _ignoreNextEngineDrawFrame = false;
  969       return;
  970     }
  971     handleDrawFrame();
  972   }
  973 
  974   /// Called by the engine to prepare the framework to produce a new frame.
  975   ///
  976   /// This function calls all the transient frame callbacks registered by
  977   /// [scheduleFrameCallback]. It then returns, any scheduled microtasks are run
  978   /// (e.g. handlers for any [Future]s resolved by transient frame callbacks),
  979   /// and [handleDrawFrame] is called to continue the frame.
  980   ///
  981   /// If the given time stamp is null, the time stamp from the last frame is
  982   /// reused.
  983   ///
  984   /// To have a banner shown at the start of every frame in debug mode, set
  985   /// [debugPrintBeginFrameBanner] to true. The banner will be printed to the
  986   /// console using [debugPrint] and will contain the frame number (which
  987   /// increments by one for each frame), and the time stamp of the frame. If the
  988   /// given time stamp was null, then the string "warm-up frame" is shown
  989   /// instead of the time stamp. This allows frames eagerly pushed by the
  990   /// framework to be distinguished from those requested by the engine in
  991   /// response to the "Vsync" signal from the operating system.
  992   ///
  993   /// You can also show a banner at the end of every frame by setting
  994   /// [debugPrintEndFrameBanner] to true. This allows you to distinguish log
  995   /// statements printed during a frame from those printed between frames (e.g.
  996   /// in response to events or timers).
  997   void handleBeginFrame(Duration? rawTimeStamp) {
  998     Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
  999     _firstRawTimeStampInEpoch ??= rawTimeStamp;
 1000     _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
 1001     if (rawTimeStamp != null)
 1002       _lastRawTimeStamp = rawTimeStamp;
 1003 
 1004     assert(() {
 1005       _debugFrameNumber += 1;
 1006 
 1007       if (debugPrintBeginFrameBanner || debugPrintEndFrameBanner) {
 1008         final StringBuffer frameTimeStampDescription = StringBuffer();
 1009         if (rawTimeStamp != null) {
 1010           _debugDescribeTimeStamp(_currentFrameTimeStamp!, frameTimeStampDescription);
 1011         } else {
 1012           frameTimeStampDescription.write('(warm-up frame)');
 1013         }
 1014         _debugBanner = '▄▄▄▄▄▄▄▄ Frame ${_debugFrameNumber.toString().padRight(7)}   ${frameTimeStampDescription.toString().padLeft(18)} ▄▄▄▄▄▄▄▄';
 1015         if (debugPrintBeginFrameBanner)
 1016           debugPrint(_debugBanner);
 1017       }
 1018       return true;
 1019     }());
 1020 
 1021     assert(schedulerPhase == SchedulerPhase.idle);
 1022     _hasScheduledFrame = false;
 1023     try {
 1024       // TRANSIENT FRAME CALLBACKS
 1025       Timeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
 1026       _schedulerPhase = SchedulerPhase.transientCallbacks;
 1027       final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
 1028       _transientCallbacks = <int, _FrameCallbackEntry>{};
 1029       callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
 1030         if (!_removedIds.contains(id))
 1031           _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
 1032       });
 1033       _removedIds.clear();
 1034     } finally {
 1035       _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
 1036     }
 1037   }
 1038 
 1039   /// Called by the engine to produce a new frame.
 1040   ///
 1041   /// This method is called immediately after [handleBeginFrame]. It calls all
 1042   /// the callbacks registered by [addPersistentFrameCallback], which typically
 1043   /// drive the rendering pipeline, and then calls the callbacks registered by
 1044   /// [addPostFrameCallback].
 1045   ///
 1046   /// See [handleBeginFrame] for a discussion about debugging hooks that may be
 1047   /// useful when working with frame callbacks.
 1048   void handleDrawFrame() {
 1049     assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
 1050     Timeline.finishSync(); // end the "Animate" phase
 1051     try {
 1052       // PERSISTENT FRAME CALLBACKS
 1053       _schedulerPhase = SchedulerPhase.persistentCallbacks;
 1054       for (final FrameCallback callback in _persistentCallbacks)
 1055         _invokeFrameCallback(callback, _currentFrameTimeStamp!);
 1056 
 1057       // POST-FRAME CALLBACKS
 1058       _schedulerPhase = SchedulerPhase.postFrameCallbacks;
 1059       final List<FrameCallback> localPostFrameCallbacks =
 1060           List<FrameCallback>.from(_postFrameCallbacks);
 1061       _postFrameCallbacks.clear();
 1062       for (final FrameCallback callback in localPostFrameCallbacks)
 1063         _invokeFrameCallback(callback, _currentFrameTimeStamp!);
 1064     } finally {
 1065       _schedulerPhase = SchedulerPhase.idle;
 1066       Timeline.finishSync(); // end the Frame
 1067       assert(() {
 1068         if (debugPrintEndFrameBanner)
 1069           debugPrint('▀' * _debugBanner!.length);
 1070         _debugBanner = null;
 1071         return true;
 1072       }());
 1073       _currentFrameTimeStamp = null;
 1074     }
 1075   }
 1076 
 1077   void _profileFramePostEvent(int frameNumber, FrameTiming frameTiming) {
 1078     postEvent('Flutter.Frame', <String, dynamic>{
 1079       'number': frameNumber,
 1080       'startTime': frameTiming.timestampInMicroseconds(FramePhase.buildStart),
 1081       'elapsed': frameTiming.totalSpan.inMicroseconds,
 1082       'build': frameTiming.buildDuration.inMicroseconds,
 1083       'raster': frameTiming.rasterDuration.inMicroseconds,
 1084       'vsyncOverhead': frameTiming.vsyncOverhead.inMicroseconds,
 1085     });
 1086   }
 1087 
 1088   static void _debugDescribeTimeStamp(Duration timeStamp, StringBuffer buffer) {
 1089     if (timeStamp.inDays > 0)
 1090       buffer.write('${timeStamp.inDays}d ');
 1091     if (timeStamp.inHours > 0)
 1092       buffer.write('${timeStamp.inHours - timeStamp.inDays * Duration.hoursPerDay}h ');
 1093     if (timeStamp.inMinutes > 0)
 1094       buffer.write('${timeStamp.inMinutes - timeStamp.inHours * Duration.minutesPerHour}m ');
 1095     if (timeStamp.inSeconds > 0)
 1096       buffer.write('${timeStamp.inSeconds - timeStamp.inMinutes * Duration.secondsPerMinute}s ');
 1097     buffer.write('${timeStamp.inMilliseconds - timeStamp.inSeconds * Duration.millisecondsPerSecond}');
 1098     final int microseconds = timeStamp.inMicroseconds - timeStamp.inMilliseconds * Duration.microsecondsPerMillisecond;
 1099     if (microseconds > 0)
 1100       buffer.write('.${microseconds.toString().padLeft(3, "0")}');
 1101     buffer.write('ms');
 1102   }
 1103 
 1104   // Calls the given [callback] with [timestamp] as argument.
 1105   //
 1106   // Wraps the callback in a try/catch and forwards any error to
 1107   // [debugSchedulerExceptionHandler], if set. If not set, then simply prints
 1108   // the error.
 1109   void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace? callbackStack ]) {
 1110     assert(callback != null);
 1111     assert(_FrameCallbackEntry.debugCurrentCallbackStack == null);
 1112     assert(() {
 1113       _FrameCallbackEntry.debugCurrentCallbackStack = callbackStack;
 1114       return true;
 1115     }());
 1116     try {
 1117       callback(timeStamp);
 1118     } catch (exception, exceptionStack) {
 1119       FlutterError.reportError(FlutterErrorDetails(
 1120         exception: exception,
 1121         stack: exceptionStack,
 1122         library: 'scheduler library',
 1123         context: ErrorDescription('during a scheduler callback'),
 1124         informationCollector: (callbackStack == null) ? null : () sync* {
 1125           yield DiagnosticsStackTrace(
 1126             '\nThis exception was thrown in the context of a scheduler callback. '
 1127             'When the scheduler callback was _registered_ (as opposed to when the '
 1128             'exception was thrown), this was the stack',
 1129             callbackStack,
 1130           );
 1131         },
 1132       ));
 1133     }
 1134     assert(() {
 1135       _FrameCallbackEntry.debugCurrentCallbackStack = null;
 1136       return true;
 1137     }());
 1138   }
 1139 }
 1140 
 1141 /// The default [SchedulingStrategy] for [SchedulerBinding.schedulingStrategy].
 1142 ///
 1143 /// If there are any frame callbacks registered, only runs tasks with
 1144 /// a [Priority] of [Priority.animation] or higher. Otherwise, runs
 1145 /// all tasks.
 1146 bool defaultSchedulingStrategy({ required int priority, required SchedulerBinding scheduler }) {
 1147   if (scheduler.transientCallbackCount > 0)
 1148     return priority >= Priority.animation.value;
 1149   return true;
 1150 }