mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
LookupBoundary (#116429)
* LookupBoundary simplified * tests * doc and impl complete * doc fixes * add more tests * review * empty
This commit is contained in:
parent
cc256c3e33
commit
297f094c01
@ -2020,6 +2020,13 @@ class _InactiveElements {
|
|||||||
/// this callback.
|
/// this callback.
|
||||||
typedef ElementVisitor = void Function(Element element);
|
typedef ElementVisitor = void Function(Element element);
|
||||||
|
|
||||||
|
/// Signature for the callback to [BuildContext.visitAncestorElements].
|
||||||
|
///
|
||||||
|
/// The argument is the ancestor being visited.
|
||||||
|
///
|
||||||
|
/// Return false to stop the walk.
|
||||||
|
typedef ConditionalElementVisitor = bool Function(Element element);
|
||||||
|
|
||||||
/// A handle to the location of a widget in the widget tree.
|
/// A handle to the location of a widget in the widget tree.
|
||||||
///
|
///
|
||||||
/// This class presents a set of methods that can be used from
|
/// This class presents a set of methods that can be used from
|
||||||
@ -2221,7 +2228,7 @@ abstract class BuildContext {
|
|||||||
///
|
///
|
||||||
/// All of the qualifications about when [dependOnInheritedWidgetOfExactType] can
|
/// All of the qualifications about when [dependOnInheritedWidgetOfExactType] can
|
||||||
/// be called apply to this method as well.
|
/// be called apply to this method as well.
|
||||||
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
|
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect });
|
||||||
|
|
||||||
/// Obtains the nearest widget of the given type `T`, which must be the type of a
|
/// Obtains the nearest widget of the given type `T`, which must be the type of a
|
||||||
/// concrete [InheritedWidget] subclass, and registers this build context with
|
/// concrete [InheritedWidget] subclass, and registers this build context with
|
||||||
@ -2229,6 +2236,7 @@ abstract class BuildContext {
|
|||||||
/// type is introduced, or the widget goes away), this build context is
|
/// type is introduced, or the widget goes away), this build context is
|
||||||
/// rebuilt so that it can obtain new values from that widget.
|
/// rebuilt so that it can obtain new values from that widget.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.BuildContext.dependOnInheritedWidgetOfExactType}
|
||||||
/// This is typically called implicitly from `of()` static methods, e.g.
|
/// This is typically called implicitly from `of()` static methods, e.g.
|
||||||
/// [Theme.of].
|
/// [Theme.of].
|
||||||
///
|
///
|
||||||
@ -2262,6 +2270,7 @@ abstract class BuildContext {
|
|||||||
/// [InheritedWidget] subclasses that supports partial updates, like
|
/// [InheritedWidget] subclasses that supports partial updates, like
|
||||||
/// [InheritedModel]. It specifies what "aspect" of the inherited
|
/// [InheritedModel]. It specifies what "aspect" of the inherited
|
||||||
/// widget this context depends on.
|
/// widget this context depends on.
|
||||||
|
/// {@endtemplate}
|
||||||
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
|
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
|
||||||
|
|
||||||
/// Obtains the element corresponding to the nearest widget of the given type `T`,
|
/// Obtains the element corresponding to the nearest widget of the given type `T`,
|
||||||
@ -2269,6 +2278,7 @@ abstract class BuildContext {
|
|||||||
///
|
///
|
||||||
/// Returns null if no such element is found.
|
/// Returns null if no such element is found.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.BuildContext.getElementForInheritedWidgetOfExactType}
|
||||||
/// Calling this method is O(1) with a small constant factor.
|
/// Calling this method is O(1) with a small constant factor.
|
||||||
///
|
///
|
||||||
/// This method does not establish a relationship with the target in the way
|
/// This method does not establish a relationship with the target in the way
|
||||||
@ -2280,11 +2290,13 @@ abstract class BuildContext {
|
|||||||
/// [dependOnInheritedWidgetOfExactType] in [State.didChangeDependencies]. It is
|
/// [dependOnInheritedWidgetOfExactType] in [State.didChangeDependencies]. It is
|
||||||
/// safe to use this method from [State.deactivate], which is called whenever
|
/// safe to use this method from [State.deactivate], which is called whenever
|
||||||
/// the widget is removed from the tree.
|
/// the widget is removed from the tree.
|
||||||
|
/// {@endtemplate}
|
||||||
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
|
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
|
||||||
|
|
||||||
/// Returns the nearest ancestor widget of the given type `T`, which must be the
|
/// Returns the nearest ancestor widget of the given type `T`, which must be the
|
||||||
/// type of a concrete [Widget] subclass.
|
/// type of a concrete [Widget] subclass.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.BuildContext.findAncestorWidgetOfExactType}
|
||||||
/// In general, [dependOnInheritedWidgetOfExactType] is more useful, since
|
/// In general, [dependOnInheritedWidgetOfExactType] is more useful, since
|
||||||
/// inherited widgets will trigger consumers to rebuild when they change. This
|
/// inherited widgets will trigger consumers to rebuild when they change. This
|
||||||
/// method is appropriate when used in interaction event handlers (e.g.
|
/// method is appropriate when used in interaction event handlers (e.g.
|
||||||
@ -2306,11 +2318,13 @@ abstract class BuildContext {
|
|||||||
///
|
///
|
||||||
/// Returns null if a widget of the requested type does not appear in the
|
/// Returns null if a widget of the requested type does not appear in the
|
||||||
/// ancestors of this context.
|
/// ancestors of this context.
|
||||||
|
/// {@endtemplate}
|
||||||
T? findAncestorWidgetOfExactType<T extends Widget>();
|
T? findAncestorWidgetOfExactType<T extends Widget>();
|
||||||
|
|
||||||
/// Returns the [State] object of the nearest ancestor [StatefulWidget] widget
|
/// Returns the [State] object of the nearest ancestor [StatefulWidget] widget
|
||||||
/// that is an instance of the given type `T`.
|
/// that is an instance of the given type `T`.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.BuildContext.findAncestorStateOfType}
|
||||||
/// This should not be used from build methods, because the build context will
|
/// This should not be used from build methods, because the build context will
|
||||||
/// not be rebuilt if the value that would be returned by this method changes.
|
/// not be rebuilt if the value that would be returned by this method changes.
|
||||||
/// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
/// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
||||||
@ -2332,6 +2346,7 @@ abstract class BuildContext {
|
|||||||
/// because the widget tree is no longer stable at that time. To refer to
|
/// because the widget tree is no longer stable at that time. To refer to
|
||||||
/// an ancestor from one of those methods, save a reference to the ancestor
|
/// an ancestor from one of those methods, save a reference to the ancestor
|
||||||
/// by calling [findAncestorStateOfType] in [State.didChangeDependencies].
|
/// by calling [findAncestorStateOfType] in [State.didChangeDependencies].
|
||||||
|
/// {@endtemplate}
|
||||||
///
|
///
|
||||||
/// {@tool snippet}
|
/// {@tool snippet}
|
||||||
///
|
///
|
||||||
@ -2344,17 +2359,20 @@ abstract class BuildContext {
|
|||||||
/// Returns the [State] object of the furthest ancestor [StatefulWidget] widget
|
/// Returns the [State] object of the furthest ancestor [StatefulWidget] widget
|
||||||
/// that is an instance of the given type `T`.
|
/// that is an instance of the given type `T`.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.BuildContext.findRootAncestorStateOfType}
|
||||||
/// Functions the same way as [findAncestorStateOfType] but keeps visiting subsequent
|
/// Functions the same way as [findAncestorStateOfType] but keeps visiting subsequent
|
||||||
/// ancestors until there are none of the type instance of `T` remaining.
|
/// ancestors until there are none of the type instance of `T` remaining.
|
||||||
/// Then returns the last one found.
|
/// Then returns the last one found.
|
||||||
///
|
///
|
||||||
/// This operation is O(N) as well though N is the entire widget tree rather than
|
/// This operation is O(N) as well though N is the entire widget tree rather than
|
||||||
/// a subtree.
|
/// a subtree.
|
||||||
|
/// {@endtemplate}
|
||||||
T? findRootAncestorStateOfType<T extends State>();
|
T? findRootAncestorStateOfType<T extends State>();
|
||||||
|
|
||||||
/// Returns the [RenderObject] object of the nearest ancestor [RenderObjectWidget] widget
|
/// Returns the [RenderObject] object of the nearest ancestor [RenderObjectWidget] widget
|
||||||
/// that is an instance of the given type `T`.
|
/// that is an instance of the given type `T`.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.BuildContext.findAncestorRenderObjectOfType}
|
||||||
/// This should not be used from build methods, because the build context will
|
/// This should not be used from build methods, because the build context will
|
||||||
/// not be rebuilt if the value that would be returned by this method changes.
|
/// not be rebuilt if the value that would be returned by this method changes.
|
||||||
/// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
/// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
||||||
@ -2371,13 +2389,16 @@ abstract class BuildContext {
|
|||||||
/// because the widget tree is no longer stable at that time. To refer to
|
/// because the widget tree is no longer stable at that time. To refer to
|
||||||
/// an ancestor from one of those methods, save a reference to the ancestor
|
/// an ancestor from one of those methods, save a reference to the ancestor
|
||||||
/// by calling [findAncestorRenderObjectOfType] in [State.didChangeDependencies].
|
/// by calling [findAncestorRenderObjectOfType] in [State.didChangeDependencies].
|
||||||
|
/// {@endtemplate}
|
||||||
T? findAncestorRenderObjectOfType<T extends RenderObject>();
|
T? findAncestorRenderObjectOfType<T extends RenderObject>();
|
||||||
|
|
||||||
/// Walks the ancestor chain, starting with the parent of this build context's
|
/// Walks the ancestor chain, starting with the parent of this build context's
|
||||||
/// widget, invoking the argument for each ancestor. The callback is given a
|
/// widget, invoking the argument for each ancestor.
|
||||||
/// reference to the ancestor widget's corresponding [Element] object. The
|
///
|
||||||
/// walk stops when it reaches the root widget or when the callback returns
|
/// {@template flutter.widgets.BuildContext.visitAncestorElements}
|
||||||
/// false. The callback must not return null.
|
/// The callback is given a reference to the ancestor widget's corresponding
|
||||||
|
/// [Element] object. The walk stops when it reaches the root widget or when
|
||||||
|
/// the callback returns false. The callback must not return null.
|
||||||
///
|
///
|
||||||
/// This is useful for inspecting the widget tree.
|
/// This is useful for inspecting the widget tree.
|
||||||
///
|
///
|
||||||
@ -2387,10 +2408,12 @@ abstract class BuildContext {
|
|||||||
/// because the element tree is no longer stable at that time. To refer to
|
/// because the element tree is no longer stable at that time. To refer to
|
||||||
/// an ancestor from one of those methods, save a reference to the ancestor
|
/// an ancestor from one of those methods, save a reference to the ancestor
|
||||||
/// by calling [visitAncestorElements] in [State.didChangeDependencies].
|
/// by calling [visitAncestorElements] in [State.didChangeDependencies].
|
||||||
void visitAncestorElements(bool Function(Element element) visitor);
|
/// {@endtemplate}
|
||||||
|
void visitAncestorElements(ConditionalElementVisitor visitor);
|
||||||
|
|
||||||
/// Walks the children of this widget.
|
/// Walks the children of this widget.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.BuildContext.visitChildElements}
|
||||||
/// This is useful for applying changes to children after they are built
|
/// This is useful for applying changes to children after they are built
|
||||||
/// without waiting for the next frame, especially if the children are known,
|
/// without waiting for the next frame, especially if the children are known,
|
||||||
/// and especially if there is exactly one child (as is always the case for
|
/// and especially if there is exactly one child (as is always the case for
|
||||||
@ -2408,6 +2431,7 @@ abstract class BuildContext {
|
|||||||
/// significantly cheaper to use an [InheritedWidget] and have the descendants
|
/// significantly cheaper to use an [InheritedWidget] and have the descendants
|
||||||
/// pull data down, than it is to use [visitChildElements] recursively to push
|
/// pull data down, than it is to use [visitChildElements] recursively to push
|
||||||
/// data down to them.
|
/// data down to them.
|
||||||
|
/// {@endtemplate}
|
||||||
void visitChildElements(ElementVisitor visitor);
|
void visitChildElements(ElementVisitor visitor);
|
||||||
|
|
||||||
/// Start bubbling this notification at the given build context.
|
/// Start bubbling this notification at the given build context.
|
||||||
@ -4452,7 +4476,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void visitAncestorElements(bool Function(Element element) visitor) {
|
void visitAncestorElements(ConditionalElementVisitor visitor) {
|
||||||
assert(_debugCheckStateIsActiveForAncestorLookup());
|
assert(_debugCheckStateIsActiveForAncestorLookup());
|
||||||
Element? ancestor = _parent;
|
Element? ancestor = _parent;
|
||||||
while (ancestor != null && visitor(ancestor)) {
|
while (ancestor != null && visitor(ancestor)) {
|
||||||
|
255
packages/flutter/lib/src/widgets/lookup_boundary.dart
Normal file
255
packages/flutter/lib/src/widgets/lookup_boundary.dart
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'framework.dart';
|
||||||
|
|
||||||
|
// Examples can assume:
|
||||||
|
// class MyWidget extends StatelessWidget { const MyWidget({super.key, required this.child}); final Widget child; @override Widget build(BuildContext context) => child; }
|
||||||
|
|
||||||
|
/// A lookup boundary controls what entities are visible to descendants of the
|
||||||
|
/// boundary via the static lookup methods provided by the boundary.
|
||||||
|
///
|
||||||
|
/// The static lookup methods of the boundary mirror the lookup methods by the
|
||||||
|
/// same name exposed on [BuildContext] and they can be used as direct
|
||||||
|
/// replacements. Unlike the methods on [BuildContext], these methods do not
|
||||||
|
/// find any ancestor entities of the closest [LookupBoundary] surrounding the
|
||||||
|
/// provided [BuildContext]. The root of the tree is an implicit lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@tool snippet}
|
||||||
|
/// In the example below, the [LookupBoundary.findAncestorWidgetOfExactType]
|
||||||
|
/// call returns null because the [LookupBoundary] "hides" `MyWidget` from the
|
||||||
|
/// [BuildContext] that was queried.
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// MyWidget(
|
||||||
|
/// child: LookupBoundary(
|
||||||
|
/// child: Builder(
|
||||||
|
/// builder: (BuildContext context) {
|
||||||
|
/// MyWidget? widget = LookupBoundary.findAncestorWidgetOfExactType<MyWidget>(context);
|
||||||
|
/// return Text('$widget'); // "null"
|
||||||
|
/// },
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
|
///
|
||||||
|
/// A [LookupBoundary] only affects the behavior of the static lookup methods
|
||||||
|
/// defined on the boundary. It does not affect the behavior of the lookup
|
||||||
|
/// methods defined on [BuildContext].
|
||||||
|
///
|
||||||
|
/// A [LookupBoundary] is rarely instantiated directly. They are inserted at
|
||||||
|
/// locations of the widget tree where the render tree diverges from the element
|
||||||
|
/// tree, which is rather uncommon. Such anomalies are created by
|
||||||
|
/// [RenderObjectElement]s that don't attach their [RenderObject] to the closest
|
||||||
|
/// ancestor [RenderObjectElement], e.g. because they bootstrap a separate
|
||||||
|
/// stand-alone render tree.
|
||||||
|
// TODO(goderbauer): Reference the View widget here once available.
|
||||||
|
/// This behavior breaks the assumption some widgets have about the structure of
|
||||||
|
/// the render tree: These widgets may try to reach out to an ancestor widget,
|
||||||
|
/// assuming that their associated [RenderObject]s are also ancestors, which due
|
||||||
|
/// to the anomaly may not be the case. At the point where the divergence in the
|
||||||
|
/// two trees is introduced, a [LookupBoundary] can be used to hide that ancestor
|
||||||
|
/// from the querying widget.
|
||||||
|
///
|
||||||
|
/// As an example, [Material.of] relies on lookup boundaries to hide the
|
||||||
|
/// [Material] widget from certain descendant button widget. Buttons reach out
|
||||||
|
/// to their [Material] ancestor to draw ink splashes on its associated render
|
||||||
|
/// object. This only produces the desired effect if the button render object
|
||||||
|
/// is a descendant of the [Material] render object. If the element tree and
|
||||||
|
/// the render tree are not in sync due to anomalies described above, this may
|
||||||
|
/// not be the case. To avoid incorrect visuals, the [Material] relies on
|
||||||
|
/// lookup boundaries to hide itself from descendants in subtrees with such
|
||||||
|
/// anomalies. Those subtrees are expected to introduce their own [Material]
|
||||||
|
/// widget that buttons there can utilize without crossing a lookup boundary.
|
||||||
|
class LookupBoundary extends InheritedWidget {
|
||||||
|
/// Creates a [LookupBoundary].
|
||||||
|
///
|
||||||
|
/// A none-null [child] widget must be provided.
|
||||||
|
const LookupBoundary({super.key, required super.child});
|
||||||
|
|
||||||
|
/// Obtains the nearest widget of the given type `T` within the current
|
||||||
|
/// [LookupBoundary] of `context`, which must be the type of a concrete
|
||||||
|
/// [InheritedWidget] subclass, and registers the provided build `context`
|
||||||
|
/// with that widget such that when that widget changes (or a new widget of
|
||||||
|
/// that type is introduced, or the widget goes away), the build context is
|
||||||
|
/// rebuilt so that it can obtain new values from that widget.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like
|
||||||
|
/// [BuildContext.dependOnInheritedWidgetOfExactType], except it only
|
||||||
|
/// considers [InheritedWidget]s of the specified type `T` between the
|
||||||
|
/// provided [BuildContext] and its closest [LookupBoundary] ancestor.
|
||||||
|
/// [InheritedWidget]s past that [LookupBoundary] are invisible to this
|
||||||
|
/// method. The root of the tree is treated as an implicit lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.dependOnInheritedWidgetOfExactType}
|
||||||
|
static T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>(BuildContext context, { Object? aspect }) {
|
||||||
|
// The following call makes sure that context depends on something so
|
||||||
|
// Element.didChangeDependencies is called when context moves in the tree
|
||||||
|
// even when requested dependency remains unfulfilled (i.e. null is
|
||||||
|
// returned).
|
||||||
|
context.dependOnInheritedWidgetOfExactType<LookupBoundary>();
|
||||||
|
final InheritedElement? candidate = getElementForInheritedWidgetOfExactType<T>(context);
|
||||||
|
if (candidate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
context.dependOnInheritedElement(candidate, aspect: aspect);
|
||||||
|
return candidate.widget as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains the element corresponding to the nearest widget of the given type
|
||||||
|
/// `T` within the current [LookupBoundary] of `context`.
|
||||||
|
///
|
||||||
|
/// `T` must be the type of a concrete [InheritedWidget] subclass. Returns
|
||||||
|
/// null if no such element is found.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like
|
||||||
|
/// [BuildContext.getElementForInheritedWidgetOfExactType], except it only
|
||||||
|
/// considers [InheritedWidget]s of the specified type `T` between the
|
||||||
|
/// provided [BuildContext] and its closest [LookupBoundary] ancestor.
|
||||||
|
/// [InheritedWidget]s past that [LookupBoundary] are invisible to this
|
||||||
|
/// method. The root of the tree is treated as an implicit lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.getElementForInheritedWidgetOfExactType}
|
||||||
|
static InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>(BuildContext context) {
|
||||||
|
final InheritedElement? candidate = context.getElementForInheritedWidgetOfExactType<T>();
|
||||||
|
if (candidate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Element? boundary = context.getElementForInheritedWidgetOfExactType<LookupBoundary>();
|
||||||
|
if (boundary != null && boundary.depth > candidate.depth) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the nearest ancestor widget of the given type `T` within the
|
||||||
|
/// current [LookupBoundary] of `context`.
|
||||||
|
///
|
||||||
|
/// `T` must be the type of a concrete [Widget] subclass.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like
|
||||||
|
/// [BuildContext.findAncestorWidgetOfExactType], except it only considers
|
||||||
|
/// [Widget]s of the specified type `T` between the provided [BuildContext]
|
||||||
|
/// and its closest [LookupBoundary] ancestor. [Widget]s past that
|
||||||
|
/// [LookupBoundary] are invisible to this method. The root of the tree is
|
||||||
|
/// treated as an implicit lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.findAncestorWidgetOfExactType}
|
||||||
|
static T? findAncestorWidgetOfExactType<T extends Widget>(BuildContext context) {
|
||||||
|
Element? target;
|
||||||
|
context.visitAncestorElements((Element ancestor) {
|
||||||
|
if (ancestor.widget.runtimeType == T) {
|
||||||
|
target = ancestor;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ancestor.widget.runtimeType != LookupBoundary;
|
||||||
|
});
|
||||||
|
return target?.widget as T?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [State] object of the nearest ancestor [StatefulWidget] widget
|
||||||
|
/// within the current [LookupBoundary] of `context` that is an instance of
|
||||||
|
/// the given type `T`.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like
|
||||||
|
/// [BuildContext.findAncestorWidgetOfExactType], except it only considers
|
||||||
|
/// [State] objects of the specified type `T` between the provided
|
||||||
|
/// [BuildContext] and its closest [LookupBoundary] ancestor. [State] objects
|
||||||
|
/// past that [LookupBoundary] are invisible to this method. The root of the
|
||||||
|
/// tree is treated as an implicit lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.findAncestorStateOfType}
|
||||||
|
static T? findAncestorStateOfType<T extends State>(BuildContext context) {
|
||||||
|
StatefulElement? target;
|
||||||
|
context.visitAncestorElements((Element ancestor) {
|
||||||
|
if (ancestor is StatefulElement && ancestor.state is T) {
|
||||||
|
target = ancestor;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ancestor.widget.runtimeType != LookupBoundary;
|
||||||
|
});
|
||||||
|
return target?.state as T?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [State] object of the furthest ancestor [StatefulWidget]
|
||||||
|
/// widget within the current [LookupBoundary] of `context` that is an
|
||||||
|
/// instance of the given type `T`.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like
|
||||||
|
/// [BuildContext.findRootAncestorStateOfType], except it considers the
|
||||||
|
/// closest [LookupBoundary] ancestor of `context` to be the root. [State]
|
||||||
|
/// objects past that [LookupBoundary] are invisible to this method. The root
|
||||||
|
/// of the tree is treated as an implicit lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.findRootAncestorStateOfType}
|
||||||
|
static T? findRootAncestorStateOfType<T extends State>(BuildContext context) {
|
||||||
|
StatefulElement? target;
|
||||||
|
context.visitAncestorElements((Element ancestor) {
|
||||||
|
if (ancestor is StatefulElement && ancestor.state is T) {
|
||||||
|
target = ancestor;
|
||||||
|
}
|
||||||
|
return ancestor.widget.runtimeType != LookupBoundary;
|
||||||
|
});
|
||||||
|
return target?.state as T?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [RenderObject] object of the nearest ancestor
|
||||||
|
/// [RenderObjectWidget] widget within the current [LookupBoundary] of
|
||||||
|
/// `context` that is an instance of the given type `T`.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like
|
||||||
|
/// [BuildContext.findAncestorRenderObjectOfType], except it only considers
|
||||||
|
/// [RenderObject]s of the specified type `T` between the provided
|
||||||
|
/// [BuildContext] and its closest [LookupBoundary] ancestor. [RenderObject]s
|
||||||
|
/// past that [LookupBoundary] are invisible to this method. The root of the
|
||||||
|
/// tree is treated as an implicit lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.findAncestorRenderObjectOfType}
|
||||||
|
static T? findAncestorRenderObjectOfType<T extends RenderObject>(BuildContext context) {
|
||||||
|
Element? target;
|
||||||
|
context.visitAncestorElements((Element ancestor) {
|
||||||
|
if (ancestor is RenderObjectElement && ancestor.renderObject is T) {
|
||||||
|
target = ancestor;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ancestor.widget.runtimeType != LookupBoundary;
|
||||||
|
});
|
||||||
|
return target?.renderObject as T?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walks the ancestor chain, starting with the parent of the build context's
|
||||||
|
/// widget, invoking the argument for each ancestor until a [LookupBoundary]
|
||||||
|
/// or the root is reached.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like [BuildContext.visitAncestorElements],
|
||||||
|
/// except it only walks the tree up to the closest [LookupBoundary] ancestor
|
||||||
|
/// of the provided context. The root of the tree is treated as an implicit
|
||||||
|
/// lookup boundary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.visitAncestorElements}
|
||||||
|
static void visitAncestorElements(BuildContext context, ConditionalElementVisitor visitor) {
|
||||||
|
context.visitAncestorElements((Element ancestor) {
|
||||||
|
return visitor(ancestor) && ancestor.widget.runtimeType != LookupBoundary;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walks the non-[LookupBoundary] child [Element]s of the provided
|
||||||
|
/// `context`.
|
||||||
|
///
|
||||||
|
/// This method behaves exactly like [BuildContext.visitChildElements],
|
||||||
|
/// except it only visits children that are not a [LookupBoundary].
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.BuildContext.visitChildElements}
|
||||||
|
static void visitChildElements(BuildContext context, ElementVisitor visitor) {
|
||||||
|
context.visitChildElements((Element child) {
|
||||||
|
if (child.widget.runtimeType != LookupBoundary) {
|
||||||
|
visitor(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
|
||||||
|
}
|
@ -72,6 +72,7 @@ export 'src/widgets/keyboard_listener.dart';
|
|||||||
export 'src/widgets/layout_builder.dart';
|
export 'src/widgets/layout_builder.dart';
|
||||||
export 'src/widgets/list_wheel_scroll_view.dart';
|
export 'src/widgets/list_wheel_scroll_view.dart';
|
||||||
export 'src/widgets/localizations.dart';
|
export 'src/widgets/localizations.dart';
|
||||||
|
export 'src/widgets/lookup_boundary.dart';
|
||||||
export 'src/widgets/magnifier.dart';
|
export 'src/widgets/magnifier.dart';
|
||||||
export 'src/widgets/media_query.dart';
|
export 'src/widgets/media_query.dart';
|
||||||
export 'src/widgets/modal_barrier.dart';
|
export 'src/widgets/modal_barrier.dart';
|
||||||
|
1020
packages/flutter/test/widgets/lookup_boundary_test.dart
Normal file
1020
packages/flutter/test/widgets/lookup_boundary_test.dart
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user