Improve performance of debugCheckHasDirectionality (#81431)
diff --git a/packages/flutter/lib/src/material/debug.dart b/packages/flutter/lib/src/material/debug.dart
index af196d6..ef61d80 100644
--- a/packages/flutter/lib/src/material/debug.dart
+++ b/packages/flutter/lib/src/material/debug.dart
@@ -20,6 +20,8 @@
/// assert(debugCheckHasMaterial(context));
/// ```
///
+/// This method can be expensive (it walks the element tree).
+///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasMaterial(BuildContext context) {
assert(() {
@@ -62,6 +64,11 @@
/// assert(debugCheckHasMaterialLocalizations(context));
/// ```
///
+/// This function has the side-effect of establishing an inheritance
+/// relationship with the nearest [Localizations] widget (see
+/// [BuildContext.dependOnInheritedWidgetOfExactType]). This is ok if the caller
+/// always also calls [Localizations.of] or [Localizations.localeOf].
+///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasMaterialLocalizations(BuildContext context) {
assert(() {
@@ -102,6 +109,8 @@
/// assert(debugCheckHasScaffold(context));
/// ```
///
+/// This method can be expensive (it walks the element tree).
+///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasScaffold(BuildContext context) {
assert(() {
@@ -133,6 +142,8 @@
/// assert(debugCheckHasScaffoldMessenger(context));
/// ```
///
+/// This method can be expensive (it walks the element tree).
+///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasScaffoldMessenger(BuildContext context) {
assert(() {
diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart
index 8957157..d05b347 100644
--- a/packages/flutter/lib/src/material/material.dart
+++ b/packages/flutter/lib/src/material/material.dart
@@ -310,6 +310,8 @@
/// ```dart
/// MaterialInkController inkController = Material.of(context);
/// ```
+ ///
+ /// This method can be expensive (it walks the element tree).
static MaterialInkController? of(BuildContext context) {
return context.findAncestorRenderObjectOfType<_RenderInkFeatures>();
}
diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart
index 526c398..c525881 100644
--- a/packages/flutter/lib/src/material/scaffold.dart
+++ b/packages/flutter/lib/src/material/scaffold.dart
@@ -1820,6 +1820,8 @@
/// If no instance of this class encloses the given context, will cause an
/// assert in debug mode, and throw an exception in release mode.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// {@tool dartpad --template=freeform}
/// Typical usage of the [Scaffold.of] function is to call it from within the
/// `build` method of a child of a [Scaffold].
@@ -1999,6 +2001,8 @@
/// If no instance of this class encloses the given context, will return null.
/// To throw an exception instead, use [of] instead of this function.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [of], a similar function to this one that throws if no instance
@@ -2066,6 +2070,8 @@
/// [Scaffold] so that the client widget gets rebuilt whenever the [hasDrawer]
/// value changes.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [Scaffold.of], which provides access to the [ScaffoldState] object as a
diff --git a/packages/flutter/lib/src/widgets/animated_list.dart b/packages/flutter/lib/src/widgets/animated_list.dart
index 4af0762..07a4f66 100644
--- a/packages/flutter/lib/src/widgets/animated_list.dart
+++ b/packages/flutter/lib/src/widgets/animated_list.dart
@@ -392,6 +392,8 @@
/// If no [AnimatedList] surrounds the context given, then this function will
/// assert in debug mode and throw an exception in release mode.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
@@ -429,6 +431,8 @@
/// If no [AnimatedList] surrounds the context given, then this function will
/// return null.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [of], a similar function that will throw if no [AnimatedList] ancestor
@@ -808,6 +812,8 @@
/// If no [SliverAnimatedList] surrounds the context given, then this function
/// will assert in debug mode and throw an exception in release mode.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
@@ -843,6 +849,8 @@
/// If no [SliverAnimatedList] surrounds the context given, then this function
/// will return null.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [of], a similar function that will throw if no [SliverAnimatedList]
diff --git a/packages/flutter/lib/src/widgets/debug.dart b/packages/flutter/lib/src/widgets/debug.dart
index cbb025a..8c364c2 100644
--- a/packages/flutter/lib/src/widgets/debug.dart
+++ b/packages/flutter/lib/src/widgets/debug.dart
@@ -184,6 +184,8 @@
/// assert(debugCheckHasTable(context));
/// ```
///
+/// This method can be expensive (it walks the element tree).
+///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasTable(BuildContext context) {
assert(() {
@@ -215,7 +217,7 @@
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasMediaQuery(BuildContext context) {
assert(() {
- if (context.widget is! MediaQuery && context.findAncestorWidgetOfExactType<MediaQuery>() == null) {
+ if (context.widget is! MediaQuery && context.getElementForInheritedWidgetOfExactType<MediaQuery>() == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('No MediaQuery widget ancestor found.'),
ErrorDescription('${context.widget.runtimeType} widgets require a MediaQuery widget ancestor.'),
@@ -267,7 +269,7 @@
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasDirectionality(BuildContext context, { String? why, String? hint, String? alternative }) {
assert(() {
- if (context.widget is! Directionality && context.findAncestorWidgetOfExactType<Directionality>() == null) {
+ if (context.widget is! Directionality && context.getElementForInheritedWidgetOfExactType<Directionality>() == null) {
why = why == null ? '' : ' $why';
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('No Directionality widget found.'),
@@ -373,6 +375,8 @@
/// assert(debugCheckHasOverlay(context));
/// ```
///
+/// This method can be expensive (it walks the element tree).
+///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasOverlay(BuildContext context) {
assert(() {
diff --git a/packages/flutter/lib/src/widgets/inherited_theme.dart b/packages/flutter/lib/src/widgets/inherited_theme.dart
index 9cf746f..c49bd9c 100644
--- a/packages/flutter/lib/src/widgets/inherited_theme.dart
+++ b/packages/flutter/lib/src/widgets/inherited_theme.dart
@@ -146,6 +146,9 @@
/// this method is called again to re-capture the updated themes.
///
/// To wrap a [Widget] in the captured themes, call [CapturedThemes.wrap].
+ ///
+ /// This method can be expensive if there are many widgets between `from` and
+ /// `to` (it walks the element tree between those nodes).
static CapturedThemes capture({ required BuildContext from, required BuildContext? to }) {
assert(from != null);
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart
index 0426044..681e0c8 100644
--- a/packages/flutter/lib/src/widgets/navigator.dart
+++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -2712,6 +2712,8 @@
///
/// If there is no [Navigator] in the give `context`, this function will throw
/// a [FlutterError] in debug mode, and an exception in release mode.
+ ///
+ /// This method can be expensive (it walks the element tree).
static NavigatorState of(
BuildContext context, {
bool rootNavigator = false,
@@ -2760,6 +2762,8 @@
/// subsequent instances of [Navigator].
///
/// Will return null if there is no ancestor [Navigator] in the `context`.
+ ///
+ /// This method can be expensive (it walks the element tree).
static NavigatorState? maybeOf(
BuildContext context, {
bool rootNavigator = false,
diff --git a/packages/flutter/lib/src/widgets/overlay.dart b/packages/flutter/lib/src/widgets/overlay.dart
index 989f05e..104f5cc 100644
--- a/packages/flutter/lib/src/widgets/overlay.dart
+++ b/packages/flutter/lib/src/widgets/overlay.dart
@@ -284,6 +284,8 @@
/// If `rootOverlay` is set to true, the state from the furthest instance of
/// this class is given instead. Useful for installing overlay entries
/// above all subsequent instances of [Overlay].
+ ///
+ /// This method can be expensive (it walks the element tree).
static OverlayState? of(
BuildContext context, {
bool rootOverlay = false,
diff --git a/packages/flutter/lib/src/widgets/page_storage.dart b/packages/flutter/lib/src/widgets/page_storage.dart
index b770da2..6f203a2 100644
--- a/packages/flutter/lib/src/widgets/page_storage.dart
+++ b/packages/flutter/lib/src/widgets/page_storage.dart
@@ -267,6 +267,8 @@
/// ```dart
/// PageStorageBucket bucket = PageStorage.of(context);
/// ```
+ ///
+ /// This method can be expensive (it walks the element tree).
static PageStorageBucket? of(BuildContext context) {
final PageStorage? widget = context.findAncestorWidgetOfExactType<PageStorage>();
return widget?.bucket;
diff --git a/packages/flutter/lib/src/widgets/reorderable_list.dart b/packages/flutter/lib/src/widgets/reorderable_list.dart
index f1bddac..ea82c74 100644
--- a/packages/flutter/lib/src/widgets/reorderable_list.dart
+++ b/packages/flutter/lib/src/widgets/reorderable_list.dart
@@ -233,6 +233,8 @@
/// If no [ReorderableList] surrounds the given context, then this function
/// will assert in debug mode and throw an exception in release mode.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
@@ -270,6 +272,8 @@
/// If no [ReorderableList] surrounds the context given, then this function will
/// return null.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [of], a similar function that will throw if no [ReorderableList] ancestor
@@ -435,6 +439,8 @@
/// If no [SliverReorderableList] surrounds the context given, this function
/// will assert in debug mode and throw an exception in release mode.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
@@ -474,6 +480,8 @@
/// If no [SliverReorderableList] surrounds the context given, this function
/// will return null.
///
+ /// This method can be expensive (it walks the element tree).
+ ///
/// See also:
///
/// * [of], a similar function that will throw if no [SliverReorderableList]