Add timeline events for post frame callbacks (#136435)

Before this change, long-running post-frame callbacks wouldn't show up in the timeline at all.  This adds a timeline event for post-frame callbacks, with a debug flag that will add timeline events for each individual callback.

#testexempt -- we have no way to test calls to the timeline.
diff --git a/packages/flutter/lib/src/cupertino/context_menu.dart b/packages/flutter/lib/src/cupertino/context_menu.dart
index 385bae1..5361c2f 100644
--- a/packages/flutter/lib/src/cupertino/context_menu.dart
+++ b/packages/flutter/lib/src/cupertino/context_menu.dart
@@ -585,7 +585,7 @@
           _lastOverlayEntry?.dispose();
           _lastOverlayEntry = null;
           _openController.reset();
-        });
+        }, debugLabel: 'removeContextMenuDecoy');
 
       case AnimationStatus.forward:
       case AnimationStatus.reverse:
@@ -1009,7 +1009,7 @@
       _updateTweenRects();
       _internalOffstage = false;
       _setOffstageInternally();
-    });
+    }, debugLabel: 'renderContextMenuRouteOffstage');
     return super.didPush();
   }
 
diff --git a/packages/flutter/lib/src/cupertino/date_picker.dart b/packages/flutter/lib/src/cupertino/date_picker.dart
index dc15ea3..a76da3f 100644
--- a/packages/flutter/lib/src/cupertino/date_picker.dart
+++ b/packages/flutter/lib/src/cupertino/date_picker.dart
@@ -1007,7 +1007,7 @@
         final int position = minCheck ? positionDouble.ceil() : positionDouble.floor();
         _animateColumnControllerToItem(minuteController, position);
       }
-    });
+    }, debugLabel: 'DatePicker.scrollToDate');
   }
 
   @override
@@ -1401,7 +1401,7 @@
       if (selectedDay != newDate.day) {
         _animateColumnControllerToItem(dayController, newDate.day - 1);
       }
-    });
+    }, debugLabel: 'DatePicker.scrollToDate');
   }
 
   @override
@@ -1725,7 +1725,7 @@
       if (selectedMonth != newDate.month) {
         _animateColumnControllerToItem(monthController, newDate.month - 1);
       }
-    });
+    }, debugLabel: 'DatePicker.scrollToDate');
   }
 
   @override
diff --git a/packages/flutter/lib/src/cupertino/refresh.dart b/packages/flutter/lib/src/cupertino/refresh.dart
index c4e1852..507758b 100644
--- a/packages/flutter/lib/src/cupertino/refresh.dart
+++ b/packages/flutter/lib/src/cupertino/refresh.dart
@@ -461,7 +461,7 @@
       } else {
         SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
           setState(() => hasSliverLayoutExtent = false);
-        });
+        }, debugLabel: 'Refresh.goToDone');
       }
     }
 
@@ -497,7 +497,7 @@
                 }
               });
               setState(() => hasSliverLayoutExtent = true);
-            });
+            }, debugLabel: 'Refresh.transition');
           }
           return RefreshIndicatorMode.armed;
         }
diff --git a/packages/flutter/lib/src/cupertino/spell_check_suggestions_toolbar.dart b/packages/flutter/lib/src/cupertino/spell_check_suggestions_toolbar.dart
index 641780e..2e100c1 100644
--- a/packages/flutter/lib/src/cupertino/spell_check_suggestions_toolbar.dart
+++ b/packages/flutter/lib/src/cupertino/spell_check_suggestions_toolbar.dart
@@ -130,7 +130,7 @@
       if (editableTextState.mounted) {
         editableTextState.bringIntoView(editableTextState.textEditingValue.selection.extent);
       }
-    });
+    }, debugLabel: 'SpellCheckSuggestions.bringIntoView');
     editableTextState.hideToolbar();
   }
 
diff --git a/packages/flutter/lib/src/gestures/binding.dart b/packages/flutter/lib/src/gestures/binding.dart
index ccb6be3..0dec4b0 100644
--- a/packages/flutter/lib/src/gestures/binding.dart
+++ b/packages/flutter/lib/src/gestures/binding.dart
@@ -175,7 +175,7 @@
         _timer = Timer.periodic(_samplingInterval, (_) => _onSampleTimeChanged());
         // Trigger an immediate sample time change.
         _onSampleTimeChanged();
-      });
+      }, debugLabel: 'Resampler.startTimer');
     }
   }
 
diff --git a/packages/flutter/lib/src/material/autocomplete.dart b/packages/flutter/lib/src/material/autocomplete.dart
index c078018..aea1276 100644
--- a/packages/flutter/lib/src/material/autocomplete.dart
+++ b/packages/flutter/lib/src/material/autocomplete.dart
@@ -201,7 +201,7 @@
                     if (highlight) {
                       SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
                         Scrollable.ensureVisible(context, alignment: 0.5);
-                      });
+                      }, debugLabel: 'AutocompleteOptions.ensureVisible');
                     }
                     return Container(
                       color: highlight ? Theme.of(context).focusColor : null,
diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart
index 0b74b39..1966b5f 100644
--- a/packages/flutter/lib/src/material/dropdown_menu.dart
+++ b/packages/flutter/lib/src/material/dropdown_menu.dart
@@ -423,7 +423,7 @@
       setState(() {
         leadingPadding = getWidth(_leadingKey);
       });
-    });
+    }, debugLabel: 'DropdownMenu.refreshLeadingPadding');
   }
 
   void scrollToHighlight() {
@@ -432,7 +432,7 @@
       if (highlightContext != null) {
         Scrollable.ensureVisible(highlightContext);
       }
-    });
+    }, debugLabel: 'DropdownMenu.scrollToHighlight');
   }
 
   double? getWidth(GlobalKey key) {
diff --git a/packages/flutter/lib/src/material/input_date_picker_form_field.dart b/packages/flutter/lib/src/material/input_date_picker_form_field.dart
index a7600e4..5bd0167 100644
--- a/packages/flutter/lib/src/material/input_date_picker_form_field.dart
+++ b/packages/flutter/lib/src/material/input_date_picker_form_field.dart
@@ -174,7 +174,7 @@
           _selectedDate = widget.initialDate;
           _updateValueForSelectedDate();
         });
-      });
+      }, debugLabel: 'InputDatePickerFormField.update');
     }
   }
 
diff --git a/packages/flutter/lib/src/material/menu_anchor.dart b/packages/flutter/lib/src/material/menu_anchor.dart
index 2f0c3a4..aa54f3b 100644
--- a/packages/flutter/lib/src/material/menu_anchor.dart
+++ b/packages/flutter/lib/src/material/menu_anchor.dart
@@ -583,7 +583,7 @@
     } else if (!inDispose) {
       SchedulerBinding.instance.addPostFrameCallback((_) {
         _overlayController.hide();
-      });
+      }, debugLabel: 'MenuAnchor.hide');
     }
     if (!inDispose) {
       // Notify that _childIsOpen changed state, but only if not
@@ -1150,7 +1150,7 @@
     SchedulerBinding.instance.addPostFrameCallback((Duration _) {
       FocusManager.instance.applyFocusChangesIfNeeded();
       widget.onPressed?.call();
-    });
+    }, debugLabel: 'MenuAnchor.onPressed');
   }
 
   void _createInternalFocusNodeIfNeeded() {
@@ -1902,7 +1902,7 @@
           SchedulerBinding.instance.addPostFrameCallback((_) {
             _menuController._anchor?._focusButton();
             _waitingToFocusMenu = false;
-          });
+          }, debugLabel: 'MenuAnchor.focus');
           _waitingToFocusMenu = true;
         }
         setState(() { /* Rebuild with updated controller.isOpen value */ });
diff --git a/packages/flutter/lib/src/material/spell_check_suggestions_toolbar.dart b/packages/flutter/lib/src/material/spell_check_suggestions_toolbar.dart
index cf0c218..35aa913 100644
--- a/packages/flutter/lib/src/material/spell_check_suggestions_toolbar.dart
+++ b/packages/flutter/lib/src/material/spell_check_suggestions_toolbar.dart
@@ -142,7 +142,7 @@
       if (editableTextState.mounted) {
         editableTextState.bringIntoView(editableTextState.textEditingValue.selection.extent);
       }
-    });
+    }, debugLabel: 'SpellCheckerSuggestionsToolbar.bringIntoView');
     editableTextState.hideToolbar();
   }
 
diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart
index 235e611..146e5db 100644
--- a/packages/flutter/lib/src/material/tabs.dart
+++ b/packages/flutter/lib/src/material/tabs.dart
@@ -1507,7 +1507,7 @@
         }
         return true;
       }());
-    });
+    }, debugLabel: 'TabBar.tabsCountCheck');
     _debugHasScheduledValidTabsCountCheck = true;
     return true;
   }
@@ -2024,7 +2024,7 @@
         }
         return true;
       }());
-    });
+    }, debugLabel: 'TabBarView.validChildrenCountCheck');
     _debugHasScheduledValidChildrenCountCheck = true;
     return true;
   }
diff --git a/packages/flutter/lib/src/painting/decoration_image.dart b/packages/flutter/lib/src/painting/decoration_image.dart
index 62ea98b..eb9f765 100644
--- a/packages/flutter/lib/src/painting/decoration_image.dart
+++ b/packages/flutter/lib/src/painting/decoration_image.dart
@@ -655,7 +655,7 @@
           },
         );
         _pendingImageSizeInfo = <String, ImageSizeInfo>{};
-      });
+      }, debugLabel: 'paintImage.recordImageSizes');
     }
   }
 
diff --git a/packages/flutter/lib/src/painting/image_cache.dart b/packages/flutter/lib/src/painting/image_cache.dart
index f8ddad8..04b69a3 100644
--- a/packages/flutter/lib/src/painting/image_cache.dart
+++ b/packages/flutter/lib/src/painting/image_cache.dart
@@ -621,7 +621,7 @@
       assert(handle != null);
       handle?.dispose();
       handle = null;
-    });
+    }, debugLabel: 'CachedImage.disposeHandle');
   }
 }
 
diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart
index ff7d2de..82bb2bd 100644
--- a/packages/flutter/lib/src/rendering/binding.dart
+++ b/packages/flutter/lib/src/rendering/binding.dart
@@ -46,7 +46,7 @@
     addPersistentFrameCallback(_handlePersistentFrameCallback);
     initMouseTracker();
     if (kIsWeb) {
-      addPostFrameCallback(_handleWebFirstFrame);
+      addPostFrameCallback(_handleWebFirstFrame, debugLabel: 'RendererBinding.webFirstFrame');
     }
     rootPipelineOwner.attach(_manifold);
   }
@@ -463,7 +463,7 @@
         return true;
       }());
       _mouseTracker!.updateAllDevices();
-    });
+    }, debugLabel: 'RendererBinding.mouseTrackerUpdate');
   }
 
   int _firstFrameDeferredCount = 0;
diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart
index f2bd274..2bf31c4 100644
--- a/packages/flutter/lib/src/rendering/layer.dart
+++ b/packages/flutter/lib/src/rendering/layer.dart
@@ -2337,7 +2337,7 @@
       SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
         _debugLeaderCheckScheduled = false;
         assert(_debugPreviousLeaders!.isEmpty);
-      });
+      }, debugLabel: 'LayerLink.leadersCleanUpCheck');
       return true;
     }());
   }
diff --git a/packages/flutter/lib/src/rendering/platform_view.dart b/packages/flutter/lib/src/rendering/platform_view.dart
index e3f4cdb..e92489d 100644
--- a/packages/flutter/lib/src/rendering/platform_view.dart
+++ b/packages/flutter/lib/src/rendering/platform_view.dart
@@ -202,7 +202,7 @@
         // Schedule a new post frame callback.
         _setOffset();
       }
-    });
+    }, debugLabel: 'RenderAndroidView.setOffset');
   }
 
   @override
diff --git a/packages/flutter/lib/src/scheduler/binding.dart b/packages/flutter/lib/src/scheduler/binding.dart
index b2cc42b..039ddfc 100644
--- a/packages/flutter/lib/src/scheduler/binding.dart
+++ b/packages/flutter/lib/src/scheduler/binding.dart
@@ -765,6 +765,13 @@
   ///
   /// Post-frame callbacks cannot be unregistered. They are called exactly once.
   ///
+  /// In debug mode, if [debugTracePostFrameCallbacks] is set to true, then the
+  /// registered callback will show up in the timeline events chart, which can
+  /// be viewed in [DevTools](https://docs.flutter.dev/tools/devtools/overview).
+  /// In that case, the `debugLabel` argument specifies the name of the callback
+  /// as it will appear in the timeline. In profile and release builds,
+  /// post-frame are never traced, and the `debugLabel` argument is ignored.
+  ///
   /// See also:
   ///
   ///  * [scheduleFrameCallback], which registers a callback for the start of
@@ -772,7 +779,21 @@
   ///  * [WidgetsBinding.drawFrame], which explains the phases of each frame
   ///    for those apps that use Flutter widgets (and where post frame
   ///    callbacks fit into those phases).
-  void addPostFrameCallback(FrameCallback callback) {
+  void addPostFrameCallback(FrameCallback callback, {String debugLabel = 'callback'}) {
+    assert(() {
+      if (debugTracePostFrameCallbacks) {
+        final FrameCallback originalCallback = callback;
+        callback = (Duration timeStamp) {
+          Timeline.startSync(debugLabel);
+          try {
+            originalCallback(timeStamp);
+          } finally {
+            Timeline.finishSync();
+          }
+        };
+      }
+      return true;
+    }());
     _postFrameCallbacks.add(callback);
   }
 
@@ -796,7 +817,7 @@
       addPostFrameCallback((Duration timeStamp) {
         _nextFrameCompleter!.complete();
         _nextFrameCompleter = null;
-      });
+      }, debugLabel: 'SchedulerBinding.completeFrame');
     }
     return _nextFrameCompleter!.future;
   }
@@ -1127,7 +1148,7 @@
         // still be true here and cause us to skip scheduling an engine frame.
         _hasScheduledFrame = false;
         scheduleFrame();
-      });
+      }, debugLabel: 'SchedulerBinding.scheduleFrame');
       return;
     }
     handleDrawFrame();
@@ -1280,8 +1301,13 @@
       final List<FrameCallback> localPostFrameCallbacks =
           List<FrameCallback>.of(_postFrameCallbacks);
       _postFrameCallbacks.clear();
-      for (final FrameCallback callback in localPostFrameCallbacks) {
-        _invokeFrameCallback(callback, _currentFrameTimeStamp!);
+      Timeline.startSync('POST_FRAME');
+      try {
+        for (final FrameCallback callback in localPostFrameCallbacks) {
+          _invokeFrameCallback(callback, _currentFrameTimeStamp!);
+        }
+      } finally {
+        Timeline.finishSync();
       }
     } finally {
       _schedulerPhase = SchedulerPhase.idle;
diff --git a/packages/flutter/lib/src/scheduler/debug.dart b/packages/flutter/lib/src/scheduler/debug.dart
index 82af69f..3be545f 100644
--- a/packages/flutter/lib/src/scheduler/debug.dart
+++ b/packages/flutter/lib/src/scheduler/debug.dart
@@ -48,6 +48,20 @@
 /// [debugPrintScheduleBuildForStacks].
 bool debugPrintScheduleFrameStacks = false;
 
+/// Record timeline trace events for post-frame callbacks.
+///
+/// When this flag is set to false (the default), the developer timeline
+/// records when post-frame callbacks are running, but it does not tell you any
+/// information about how that time is spent within specific callbacks:
+///
+/// ![](https://flutter.github.io/assets-for-api-docs/assets/scheduler/debug_trace_post_frame_callbacks_off.png)
+///
+/// When this flag is set to true, timeline events will be recorded for each
+/// post-frame callback that runs, like so:
+///
+/// ![](https://flutter.github.io/assets-for-api-docs/assets/scheduler/debug_trace_post_frame_callbacks_on.png)
+bool debugTracePostFrameCallbacks = false;
+
 /// Returns true if none of the scheduler library debug variables have been changed.
 ///
 /// This function is used by the test framework to ensure that debug variables
diff --git a/packages/flutter/lib/src/services/restoration.dart b/packages/flutter/lib/src/services/restoration.dart
index 694f2f6..b188ec1 100644
--- a/packages/flutter/lib/src/services/restoration.dart
+++ b/packages/flutter/lib/src/services/restoration.dart
@@ -268,7 +268,7 @@
     if (_isReplacing) {
       SchedulerBinding.instance.addPostFrameCallback((Duration _) {
         _isReplacing = false;
-      });
+      }, debugLabel: 'RestorationManager.resetIsReplacing');
     }
 
     final RestorationBucket? oldRoot = _rootBucket;
@@ -349,7 +349,10 @@
     _bucketsNeedingSerialization.add(bucket);
     if (!_serializationScheduled) {
       _serializationScheduled = true;
-      SchedulerBinding.instance.addPostFrameCallback((Duration _) => _doSerialization());
+      SchedulerBinding.instance.addPostFrameCallback(
+        (Duration _) => _doSerialization(),
+        debugLabel: 'RestorationManager.doSerialization'
+      );
     }
   }
 
diff --git a/packages/flutter/lib/src/widgets/actions.dart b/packages/flutter/lib/src/widgets/actions.dart
index 6d373b8..beba13b 100644
--- a/packages/flutter/lib/src/widgets/actions.dart
+++ b/packages/flutter/lib/src/widgets/actions.dart
@@ -1231,7 +1231,7 @@
     super.initState();
     SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
       _updateHighlightMode(FocusManager.instance.highlightMode);
-    });
+    }, debugLabel: 'FocusableActionDetector.updateHighlightMode');
     FocusManager.instance.addHighlightModeListener(_handleFocusHighlightModeChange);
   }
 
@@ -1339,7 +1339,7 @@
     if (widget.enabled != oldWidget.enabled) {
       SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
         _mayTriggerCallback(oldWidget: oldWidget);
-      });
+      }, debugLabel: 'FocusableActionDetector.mayTriggerCallback');
     }
   }
 
diff --git a/packages/flutter/lib/src/widgets/autocomplete.dart b/packages/flutter/lib/src/widgets/autocomplete.dart
index d44ec4d..d71180a 100644
--- a/packages/flutter/lib/src/widgets/autocomplete.dart
+++ b/packages/flutter/lib/src/widgets/autocomplete.dart
@@ -440,7 +440,7 @@
         SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
           _floatingOptionsUpdateScheduled = false;
           _updateOverlay();
-        });
+        }, debugLabel: 'RawAutoComplete.updateOverlay');
       }
       return;
     }
diff --git a/packages/flutter/lib/src/widgets/automatic_keep_alive.dart b/packages/flutter/lib/src/widgets/automatic_keep_alive.dart
index a8292b1..bd40d2a 100644
--- a/packages/flutter/lib/src/widgets/automatic_keep_alive.dart
+++ b/packages/flutter/lib/src/widgets/automatic_keep_alive.dart
@@ -99,7 +99,7 @@
           final ParentDataElement<KeepAliveParentDataMixin>? childElement = _getChildElement();
           assert(childElement != null);
           _updateParentDataOfChild(childElement!);
-        });
+        }, debugLabel: 'AutomaticKeepAlive.updateParentData');
       }
     }
     return false;
diff --git a/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart b/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart
index eaf615a..9c7f80b 100644
--- a/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart
+++ b/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart
@@ -763,7 +763,7 @@
             _scrollController.positions.elementAt(index) as _DraggableScrollableSheetScrollPosition;
           position.goBallistic(0);
         }
-      });
+      }, debugLabel: 'DraggableScrollableSheet.snap');
     }
   }
 
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
index c4c3765..13ede28 100644
--- a/packages/flutter/lib/src/widgets/editable_text.dart
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
@@ -2390,7 +2390,7 @@
         if (mounted) {
           bringIntoView(textEditingValue.selection.extent);
         }
-      });
+      }, debugLabel: 'EditableText.bringSelectionIntoView');
       hideToolbar();
     }
     clipboardStatus.update();
@@ -2430,7 +2430,7 @@
         if (mounted) {
           bringIntoView(textEditingValue.selection.extent);
         }
-      });
+      }, debugLabel: 'EditableText.bringSelectionIntoView');
       hideToolbar();
     }
   }
@@ -2818,7 +2818,7 @@
           _flagInternalFocus();
           FocusScope.of(context).autofocus(widget.focusNode);
         }
-      });
+      }, debugLabel: 'EditableText.autofocus');
     }
 
     // Restart or stop the blinking cursor when TickerMode changes.
@@ -2889,7 +2889,7 @@
       // See https://github.com/flutter/flutter/issues/126312
       SchedulerBinding.instance.addPostFrameCallback((Duration _) {
         _openInputConnection();
-      });
+      }, debugLabel: 'EditableText.openInputConnection');
     }
 
     if (kIsWeb && _hasInputConnection) {
@@ -3721,7 +3721,7 @@
           rect: caretPadding.inflateRect(rectToReveal),
         );
       }
-    });
+    }, debugLabel: 'EditableText.showCaret');
   }
 
   late double _lastBottomViewInset;
@@ -3735,7 +3735,7 @@
     if (_lastBottomViewInset != view.viewInsets.bottom) {
       SchedulerBinding.instance.addPostFrameCallback((Duration _) {
         _selectionOverlay?.updateForScroll();
-      });
+      }, debugLabel: 'EditableText.updateForScroll');
       if (_lastBottomViewInset < view.viewInsets.bottom) {
         // Because the metrics change signal from engine will come here every frame
         // (on both iOS and Android). So we don't need to show caret with animation.
@@ -4038,7 +4038,10 @@
     _updateSelectionRects();
     _updateComposingRectIfNeeded();
     _updateCaretRectIfNeeded();
-    SchedulerBinding.instance.addPostFrameCallback(_schedulePeriodicPostFrameCallbacks);
+    SchedulerBinding.instance.addPostFrameCallback(
+      _schedulePeriodicPostFrameCallbacks,
+      debugLabel: 'EditableText.postFrameCallbacks'
+    );
   }
   _ScribbleCacheKey? _scribbleCacheKey;
 
diff --git a/packages/flutter/lib/src/widgets/heroes.dart b/packages/flutter/lib/src/widgets/heroes.dart
index 1edac3c..72a1716 100644
--- a/packages/flutter/lib/src/widgets/heroes.dart
+++ b/packages/flutter/lib/src/widgets/heroes.dart
@@ -902,7 +902,7 @@
           return;
         }
         _startHeroTransition(from, to, flightType, isUserGestureTransition);
-      });
+      }, debugLabel: 'HeroController.startTransition');
     }
   }
 
diff --git a/packages/flutter/lib/src/widgets/image.dart b/packages/flutter/lib/src/widgets/image.dart
index e2871e5..c318050 100644
--- a/packages/flutter/lib/src/widgets/image.dart
+++ b/packages/flutter/lib/src/widgets/image.dart
@@ -124,7 +124,7 @@
       // See ImageCache._liveImages
       SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
         stream.removeListener(listener!);
-      });
+      }, debugLabel: 'precacheImage.removeListener');
     },
     onError: (Object exception, StackTrace? stackTrace) {
       if (!completer.isCompleted) {
@@ -1164,7 +1164,10 @@
 
   void _replaceImage({required ImageInfo? info}) {
     final ImageInfo? oldImageInfo = _imageInfo;
-    SchedulerBinding.instance.addPostFrameCallback((_) => oldImageInfo?.dispose());
+    SchedulerBinding.instance.addPostFrameCallback(
+      (_) => oldImageInfo?.dispose(),
+      debugLabel: 'Image.disposeOldInfo'
+    );
     _imageInfo = info;
   }
 
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart
index 7394a52..7fc2966 100644
--- a/packages/flutter/lib/src/widgets/navigator.dart
+++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -3509,7 +3509,7 @@
             return;
           }
           notification.dispatch(context);
-        });
+        }, debugLabel: 'Navigator.dispatchNotification');
     }
   }
 
@@ -3716,7 +3716,7 @@
                   );
                 }
               }
-            });
+            }, debugLabel: 'Navigator.checkHeroControllerOwnership');
           }
           return true;
         }());
diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart
index 30ee29f..f222688 100644
--- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart
@@ -1183,11 +1183,9 @@
     // the position change notifications because those happen synchronously
     // during a frame, at a time where it's too late to call setState. Since the
     // result is usually animated, the lag incurred is no big deal.
-    SchedulerBinding.instance.addPostFrameCallback(
-      (Duration timeStamp) {
-        coordinator.updateShadow();
-      },
-    );
+    SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
+      coordinator.updateShadow();
+    }, debugLabel: 'NestedScrollController.updateShadow');
   }
 
   Iterable<_NestedScrollPosition> get nestedPositions {
diff --git a/packages/flutter/lib/src/widgets/overlay.dart b/packages/flutter/lib/src/widgets/overlay.dart
index 3cafb4f..cc98507 100644
--- a/packages/flutter/lib/src/widgets/overlay.dart
+++ b/packages/flutter/lib/src/widgets/overlay.dart
@@ -177,7 +177,7 @@
     if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
       SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
         overlay._markDirty();
-      });
+      }, debugLabel: 'OverlayEntry.markDirty');
     } else {
       overlay._markDirty();
     }
diff --git a/packages/flutter/lib/src/widgets/platform_view.dart b/packages/flutter/lib/src/widgets/platform_view.dart
index 9916b34..e2e2b9b 100644
--- a/packages/flutter/lib/src/widgets/platform_view.dart
+++ b/packages/flutter/lib/src/widgets/platform_view.dart
@@ -1301,7 +1301,7 @@
     // A call to `localToGlobal` requires waiting for a frame to render first.
     SchedulerBinding.instance.addPostFrameCallback((_) {
       onLayout(size, localToGlobal(Offset.zero));
-    });
+    }, debugLabel: 'PlatformViewPlaceholderBox.onLayout');
   }
 }
 
@@ -1333,6 +1333,6 @@
   void disposePostFrame() {
     SchedulerBinding.instance.addPostFrameCallback((_) {
       dispose();
-    });
+    }, debugLabel: 'PlatformViewController.dispose');
   }
 }
diff --git a/packages/flutter/lib/src/widgets/reorderable_list.dart b/packages/flutter/lib/src/widgets/reorderable_list.dart
index af912e5..8dac18e 100644
--- a/packages/flutter/lib/src/widgets/reorderable_list.dart
+++ b/packages/flutter/lib/src/widgets/reorderable_list.dart
@@ -753,7 +753,7 @@
     _dragStartTransitionComplete = false;
     SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
       _dragStartTransitionComplete = true;
-    });
+    }, debugLabel: 'SliverReorderableList.completeDragStartTransition');
 
     _insertIndex = item.index;
     _dragInfo = _DragInfo(
diff --git a/packages/flutter/lib/src/widgets/router.dart b/packages/flutter/lib/src/widgets/router.dart
index dc663c4..5541f92 100644
--- a/packages/flutter/lib/src/widgets/router.dart
+++ b/packages/flutter/lib/src/widgets/router.dart
@@ -628,7 +628,10 @@
     }
     assert(_currentIntentionToReport != null);
     _routeInformationReportingTaskScheduled = true;
-    SchedulerBinding.instance.addPostFrameCallback(_reportRouteInformation);
+    SchedulerBinding.instance.addPostFrameCallback(
+      _reportRouteInformation,
+      debugLabel: 'Router.reportRouteInfo',
+    );
   }
 
   void _reportRouteInformation(Duration timestamp) {
diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart
index 7d8729e..d8e065c 100644
--- a/packages/flutter/lib/src/widgets/routes.dart
+++ b/packages/flutter/lib/src/widgets/routes.dart
@@ -710,7 +710,7 @@
           if (isActive) {
             changedInternalState();
           }
-        });
+        }, debugLabel: 'LocalHistoryRoute.changedInternalState');
       } else {
         changedInternalState();
       }
@@ -1672,7 +1672,7 @@
             return;
           }
           notification.dispatch(subtreeContext);
-        });
+        }, debugLabel: 'ModalRoute.dispatchNotification');
     }
   }
 
diff --git a/packages/flutter/lib/src/widgets/scroll_position.dart b/packages/flutter/lib/src/widgets/scroll_position.dart
index 1a23ce9..2a03d15 100644
--- a/packages/flutter/lib/src/widgets/scroll_position.dart
+++ b/packages/flutter/lib/src/widgets/scroll_position.dart
@@ -468,7 +468,7 @@
     notifyListeners();
     SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
       _impliedVelocity = 0;
-    });
+    }, debugLabel: 'ScrollPosition.resetVelocity');
   }
 
   /// Called whenever scrolling ends, to store the current scroll offset in a
diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart
index 9393be4..5dfcb33 100644
--- a/packages/flutter/lib/src/widgets/scrollable.dart
+++ b/packages/flutter/lib/src/widgets/scrollable.dart
@@ -1167,7 +1167,7 @@
       }
       _scheduledLayoutChange = false;
       layoutDidChange();
-    });
+    }, debugLabel: 'ScrollableSelectionContainer.layoutDidChange');
   }
 
   /// Stores the scroll offset when a scrollable receives the last
diff --git a/packages/flutter/lib/src/widgets/scrollbar.dart b/packages/flutter/lib/src/widgets/scrollbar.dart
index 6b32305..76891b9 100644
--- a/packages/flutter/lib/src/widgets/scrollbar.dart
+++ b/packages/flutter/lib/src/widgets/scrollbar.dart
@@ -1413,7 +1413,7 @@
     }
     WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
       assert(_debugCheckHasValidScrollPosition());
-    });
+    }, debugLabel: 'RawScrollbar.checkScrollPosition');
     return true;
   }
 
diff --git a/packages/flutter/lib/src/widgets/selectable_region.dart b/packages/flutter/lib/src/widgets/selectable_region.dart
index 70369b8..317ed01 100644
--- a/packages/flutter/lib/src/widgets/selectable_region.dart
+++ b/packages/flutter/lib/src/widgets/selectable_region.dart
@@ -677,7 +677,7 @@
         }
         _scheduledSelectionEndEdgeUpdate = false;
         _triggerSelectionEndEdgeUpdate(textGranularity: textGranularity);
-      });
+      }, debugLabel: 'SelectableRegion.endEdgeUpdate');
       return;
     }
  }
@@ -731,7 +731,7 @@
         }
         _scheduledSelectionStartEdgeUpdate = false;
         _triggerSelectionStartEdgeUpdate(textGranularity: textGranularity);
-      });
+      }, debugLabel: 'SelectableRegion.startEdgeUpdate');
       return;
     }
   }
@@ -1716,7 +1716,10 @@
         // safely updated in the same frame in this case.
         scheduleMicrotask(runScheduledTask);
       } else {
-        SchedulerBinding.instance.addPostFrameCallback(runScheduledTask);
+        SchedulerBinding.instance.addPostFrameCallback(
+          runScheduledTask,
+          debugLabel: 'SelectionContainer.runScheduledTask',
+        );
       }
     }
   }
diff --git a/packages/flutter/lib/src/widgets/semantics_debugger.dart b/packages/flutter/lib/src/widgets/semantics_debugger.dart
index 892140f..6b6c69a 100644
--- a/packages/flutter/lib/src/widgets/semantics_debugger.dart
+++ b/packages/flutter/lib/src/widgets/semantics_debugger.dart
@@ -94,7 +94,7 @@
           // The generation of the _SemanticsDebuggerListener has changed.
         });
       }
-    });
+    }, debugLabel: 'SemanticsDebugger.update');
   }
 
   Offset? _lastPointerDownLocation;
diff --git a/packages/flutter/lib/src/widgets/shortcuts.dart b/packages/flutter/lib/src/widgets/shortcuts.dart
index 293d28e..d211817 100644
--- a/packages/flutter/lib/src/widgets/shortcuts.dart
+++ b/packages/flutter/lib/src/widgets/shortcuts.dart
@@ -1276,7 +1276,7 @@
         if (!_disposed) {
           notifyListeners();
         }
-      });
+      }, debugLabel: 'ShortcutRegistry.notifyListeners');
       _notificationScheduled = true;
     }
   }
diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart
index 0ed6718..e85365d 100644
--- a/packages/flutter/lib/src/widgets/text_selection.dart
+++ b/packages/flutter/lib/src/widgets/text_selection.dart
@@ -1454,7 +1454,7 @@
         } else if (_spellCheckToolbarController.isShown) {
           _spellCheckToolbarController.markNeedsBuild();
         }
-      });
+      }, debugLabel: 'SelectionOverlay.markNeedsBuild');
     } else {
       if (_handles != null) {
         _handles!.start.markNeedsBuild();
diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart
index 0a794e1..d6e3580 100644
--- a/packages/flutter/lib/src/widgets/widget_inspector.dart
+++ b/packages/flutter/lib/src/widgets/widget_inspector.dart
@@ -2371,7 +2371,7 @@
 
   void _onFrameStart(Duration timeStamp) {
     _frameStart = timeStamp;
-    SchedulerBinding.instance.addPostFrameCallback(_onFrameEnd);
+    SchedulerBinding.instance.addPostFrameCallback(_onFrameEnd, debugLabel: 'WidgetInspector.onFrameStart');
   }
 
   void _onFrameEnd(Duration timeStamp) {
diff --git a/packages/flutter/test/gestures/gesture_binding_resample_event_test.dart b/packages/flutter/test/gestures/gesture_binding_resample_event_test.dart
index a03c4b0..f5a7a54 100644
--- a/packages/flutter/test/gestures/gesture_binding_resample_event_test.dart
+++ b/packages/flutter/test/gestures/gesture_binding_resample_event_test.dart
@@ -33,7 +33,7 @@
   }
 
   @override
-  int addPostFrameCallback(FrameCallback callback) {
+  int addPostFrameCallback(FrameCallback callback, {String debugLabel = 'callback'}) {
     postFrameCallback = callback;
     return 0;
   }
diff --git a/packages/flutter/test/rendering/mouse_tracker_test_utils.dart b/packages/flutter/test/rendering/mouse_tracker_test_utils.dart
index eb81870..ce4938f 100644
--- a/packages/flutter/test/rendering/mouse_tracker_test_utils.dart
+++ b/packages/flutter/test/rendering/mouse_tracker_test_utils.dart
@@ -70,7 +70,7 @@
 
   // Proxy post-frame callbacks.
   @override
-  void addPostFrameCallback(void Function(Duration) callback) {
+  void addPostFrameCallback(void Function(Duration) callback, {String debugLabel = 'callback'}) {
     postFrameCallbacks.add(callback);
   }