Fix Scrollable.of()
Make ancestorStateOfType() and ancestorRenderObjectOfType() support subclassing. Fixes https://github.com/flutter/flutter/issues/1087
This commit is contained in:
parent
19fb068d83
commit
ea3864b1e4
@ -8,7 +8,7 @@ import 'material.dart';
|
||||
|
||||
bool debugCheckHasMaterial(BuildContext context) {
|
||||
assert(() {
|
||||
if (context.widget is Material || context.ancestorWidgetOfType(Material) != null)
|
||||
if (context.widget is Material || context.ancestorWidgetOfExactType(Material) != null)
|
||||
return true;
|
||||
Element element = context;
|
||||
debugPrint('${context.widget} needs to be placed inside a Material widget. Ownership chain:\n${element.debugGetOwnershipChain(10)}');
|
||||
|
@ -20,7 +20,7 @@ class IconTheme extends InheritedWidget {
|
||||
|
||||
/// The data from the closest instance of this class that encloses the given context.
|
||||
static IconThemeData of(BuildContext context) {
|
||||
IconTheme result = context.inheritFromWidgetOfType(IconTheme);
|
||||
IconTheme result = context.inheritFromWidgetOfExactType(IconTheme);
|
||||
return result?.data;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ class Material extends StatefulComponent {
|
||||
|
||||
/// The ink controller from the closest instance of this class that encloses the given context.
|
||||
static MaterialInkController of(BuildContext context) {
|
||||
final RenderInkFeatures result = context.ancestorRenderObjectOfType(RenderInkFeatures);
|
||||
final RenderInkFeatures result = context.ancestorRenderObjectOfType(const TypeMatcher<RenderInkFeatures>());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ class ButtonTheme extends InheritedWidget {
|
||||
///
|
||||
/// Defaults to [ButtonColor.normal] if none exists.
|
||||
static ButtonColor of(BuildContext context) {
|
||||
ButtonTheme result = context.inheritFromWidgetOfType(ButtonTheme);
|
||||
ButtonTheme result = context.inheritFromWidgetOfExactType(ButtonTheme);
|
||||
return result?.color ?? ButtonColor.normal;
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ class Scaffold extends StatefulComponent {
|
||||
final Widget drawer;
|
||||
|
||||
/// The state from the closest instance of this class that encloses the given context.
|
||||
static ScaffoldState of(BuildContext context) => context.ancestorStateOfType(ScaffoldState);
|
||||
static ScaffoldState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<ScaffoldState>());
|
||||
|
||||
ScaffoldState createState() => new ScaffoldState();
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class Theme extends InheritedWidget {
|
||||
///
|
||||
/// Defaults to the fallback theme data if none exists.
|
||||
static ThemeData of(BuildContext context) {
|
||||
Theme theme = context.inheritFromWidgetOfType(Theme);
|
||||
Theme theme = context.inheritFromWidgetOfExactType(Theme);
|
||||
return theme?.data ?? _kFallbackTheme;
|
||||
}
|
||||
|
||||
|
@ -1411,7 +1411,7 @@ class DefaultTextStyle extends InheritedWidget {
|
||||
|
||||
/// The style from the closest instance of this class that encloses the given context.
|
||||
static TextStyle of(BuildContext context) {
|
||||
DefaultTextStyle result = context.inheritFromWidgetOfType(DefaultTextStyle);
|
||||
DefaultTextStyle result = context.inheritFromWidgetOfExactType(DefaultTextStyle);
|
||||
return result?.style;
|
||||
}
|
||||
|
||||
@ -1737,7 +1737,7 @@ class DefaultAssetBundle extends InheritedWidget {
|
||||
|
||||
/// The bundle from the closest instance of this class that encloses the given context.
|
||||
static AssetBundle of(BuildContext context) {
|
||||
DefaultAssetBundle result = context.inheritFromWidgetOfType(DefaultAssetBundle);
|
||||
DefaultAssetBundle result = context.inheritFromWidgetOfExactType(DefaultAssetBundle);
|
||||
return result?.bundle;
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ class Focus extends StatefulComponent {
|
||||
assert(context.widget != null);
|
||||
assert(context.widget.key != null);
|
||||
assert(context.widget.key is GlobalKey);
|
||||
_FocusScope focusScope = context.inheritFromWidgetOfType(_FocusScope);
|
||||
_FocusScope focusScope = context.inheritFromWidgetOfExactType(_FocusScope);
|
||||
if (focusScope != null) {
|
||||
if (autofocus)
|
||||
focusScope._setFocusedWidgetIfUnset(context.widget.key);
|
||||
@ -113,7 +113,7 @@ class Focus extends StatefulComponent {
|
||||
assert(context != null);
|
||||
assert(context.widget != null);
|
||||
assert(context.widget is Focus);
|
||||
_FocusScope focusScope = context.inheritFromWidgetOfType(_FocusScope);
|
||||
_FocusScope focusScope = context.inheritFromWidgetOfExactType(_FocusScope);
|
||||
if (focusScope != null) {
|
||||
assert(context.widget.key != null);
|
||||
if (autofocus)
|
||||
@ -131,7 +131,7 @@ class Focus extends StatefulComponent {
|
||||
/// called from event listeners, e.g. in response to a finger tap or tab key.
|
||||
static void moveTo(GlobalKey key) {
|
||||
assert(key.currentContext != null);
|
||||
_FocusScope focusScope = key.currentContext.ancestorWidgetOfType(_FocusScope);
|
||||
_FocusScope focusScope = key.currentContext.ancestorWidgetOfExactType(_FocusScope);
|
||||
if (focusScope != null)
|
||||
focusScope.focusState._setFocusedWidget(key);
|
||||
}
|
||||
@ -144,7 +144,7 @@ class Focus extends StatefulComponent {
|
||||
static void moveScopeTo(GlobalKey key) {
|
||||
assert(key.currentWidget is Focus);
|
||||
assert(key.currentContext != null);
|
||||
_FocusScope focusScope = key.currentContext.ancestorWidgetOfType(_FocusScope);
|
||||
_FocusScope focusScope = key.currentContext.ancestorWidgetOfExactType(_FocusScope);
|
||||
if (focusScope != null)
|
||||
focusScope.focusState._setFocusedScope(key);
|
||||
}
|
||||
|
@ -198,6 +198,11 @@ class GlobalObjectKey extends GlobalKey {
|
||||
String toString() => '[$runtimeType ${value.runtimeType}(${value.hashCode})]';
|
||||
}
|
||||
|
||||
/// This class is a work-around for the "is" operator not accepting a variable value as its right operand
|
||||
class TypeMatcher<T> {
|
||||
const TypeMatcher();
|
||||
bool check(dynamic object) => object is T;
|
||||
}
|
||||
|
||||
// WIDGETS
|
||||
|
||||
@ -585,10 +590,10 @@ typedef void ElementVisitor(Element element);
|
||||
abstract class BuildContext {
|
||||
Widget get widget;
|
||||
RenderObject findRenderObject();
|
||||
InheritedWidget inheritFromWidgetOfType(Type targetType);
|
||||
Widget ancestorWidgetOfType(Type targetType);
|
||||
State ancestorStateOfType(Type targetType);
|
||||
RenderObject ancestorRenderObjectOfType(Type targetType);
|
||||
InheritedWidget inheritFromWidgetOfExactType(Type targetType);
|
||||
Widget ancestorWidgetOfExactType(Type targetType);
|
||||
State ancestorStateOfType(TypeMatcher matcher);
|
||||
RenderObject ancestorRenderObjectOfType(TypeMatcher matcher);
|
||||
void visitAncestorElements(bool visitor(Element element));
|
||||
void visitChildElements(void visitor(Element element));
|
||||
}
|
||||
@ -876,24 +881,24 @@ abstract class Element<T extends Widget> implements BuildContext {
|
||||
RenderObject findRenderObject() => renderObject;
|
||||
|
||||
Set<Type> _dependencies;
|
||||
InheritedWidget inheritFromWidgetOfType(Type targetType) {
|
||||
InheritedWidget inheritFromWidgetOfExactType(Type targetType) {
|
||||
if (_dependencies == null)
|
||||
_dependencies = new Set<Type>();
|
||||
_dependencies.add(targetType);
|
||||
return ancestorWidgetOfType(targetType);
|
||||
return ancestorWidgetOfExactType(targetType);
|
||||
}
|
||||
|
||||
Widget ancestorWidgetOfType(Type targetType) {
|
||||
Widget ancestorWidgetOfExactType(Type targetType) {
|
||||
Element ancestor = _parent;
|
||||
while (ancestor != null && ancestor.widget.runtimeType != targetType)
|
||||
ancestor = ancestor._parent;
|
||||
return ancestor?.widget;
|
||||
}
|
||||
|
||||
State ancestorStateOfType(Type targetType) {
|
||||
State ancestorStateOfType(TypeMatcher matcher) {
|
||||
Element ancestor = _parent;
|
||||
while (ancestor != null) {
|
||||
if (ancestor is StatefulComponentElement && ancestor.state.runtimeType == targetType)
|
||||
if (ancestor is StatefulComponentElement && matcher.check(ancestor.state))
|
||||
break;
|
||||
ancestor = ancestor._parent;
|
||||
}
|
||||
@ -901,10 +906,10 @@ abstract class Element<T extends Widget> implements BuildContext {
|
||||
return statefulAncestor?.state;
|
||||
}
|
||||
|
||||
RenderObject ancestorRenderObjectOfType(Type targetType) {
|
||||
RenderObject ancestorRenderObjectOfType(TypeMatcher matcher) {
|
||||
Element ancestor = _parent;
|
||||
while (ancestor != null) {
|
||||
if (ancestor is RenderObjectElement && ancestor.renderObject.runtimeType == targetType)
|
||||
if (ancestor is RenderObjectElement && matcher.check(ancestor.renderObject))
|
||||
break;
|
||||
ancestor = ancestor._parent;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class LocaleQuery<T extends LocaleQueryData> extends InheritedWidget {
|
||||
|
||||
/// The data from the closest instance of this class that encloses the given context.
|
||||
static LocaleQueryData of(BuildContext context) {
|
||||
LocaleQuery query = context.inheritFromWidgetOfType(LocaleQuery);
|
||||
LocaleQuery query = context.inheritFromWidgetOfExactType(LocaleQuery);
|
||||
return query == null ? null : query.data;
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ class MediaQuery extends InheritedWidget {
|
||||
/// When that information changes, your widget will be scheduled to be rebuilt,
|
||||
/// keeping your widget up-to-date.
|
||||
static MediaQueryData of(BuildContext context) {
|
||||
MediaQuery query = context.inheritFromWidgetOfType(MediaQuery);
|
||||
MediaQuery query = context.inheritFromWidgetOfExactType(MediaQuery);
|
||||
return query == null ? null : query.data;
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ class MimicOverlayEntry {
|
||||
}
|
||||
}
|
||||
|
||||
RenderBox stack = context.ancestorRenderObjectOfType(RenderStack);
|
||||
RenderBox stack = context.ancestorRenderObjectOfType(const TypeMatcher<RenderStack>());
|
||||
// TODO(abarth): Handle the case where the transform here isn't just a translation.
|
||||
Point localPosition = stack == null ? globalPosition: stack.globalToLocal(globalPosition);
|
||||
return new Positioned(
|
||||
|
@ -144,7 +144,7 @@ class Navigator extends StatefulComponent {
|
||||
}
|
||||
|
||||
static bool canPop(BuildContext context) {
|
||||
NavigatorState navigator = context.ancestorStateOfType(NavigatorState);
|
||||
NavigatorState navigator = context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
|
||||
return navigator != null && navigator.canPop();
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ class Navigator extends StatefulComponent {
|
||||
}
|
||||
|
||||
static void openTransaction(BuildContext context, NavigatorTransactionCallback callback) {
|
||||
NavigatorState navigator = context.ancestorStateOfType(NavigatorState);
|
||||
NavigatorState navigator = context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
|
||||
navigator.openTransaction(callback);
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ class Overlay extends StatefulComponent {
|
||||
final List<OverlayEntry> initialEntries;
|
||||
|
||||
/// The state from the closest instance of this class that encloses the given context.
|
||||
static OverlayState of(BuildContext context) => context.ancestorStateOfType(OverlayState);
|
||||
static OverlayState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<OverlayState>());
|
||||
|
||||
OverlayState createState() => new OverlayState();
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class PageStorage extends StatelessComponent {
|
||||
///
|
||||
/// Returns null if none exists.
|
||||
static PageStorageBucket of(BuildContext context) {
|
||||
PageStorage widget = context.ancestorWidgetOfType(PageStorage);
|
||||
PageStorage widget = context.ancestorWidgetOfExactType(PageStorage);
|
||||
return widget?.bucket;
|
||||
}
|
||||
|
||||
|
@ -388,7 +388,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
///
|
||||
/// Returns null if the given context is not associated with a modal route.
|
||||
static ModalRoute of(BuildContext context) {
|
||||
_ModalScopeStatus widget = context.inheritFromWidgetOfType(_ModalScopeStatus);
|
||||
_ModalScopeStatus widget = context.inheritFromWidgetOfExactType(_ModalScopeStatus);
|
||||
return widget?.route;
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ abstract class Scrollable extends StatefulComponent {
|
||||
|
||||
/// The state from the closest instance of this class that encloses the given context.
|
||||
static ScrollableState of(BuildContext context) {
|
||||
return context.ancestorStateOfType(ScrollableState);
|
||||
return context.ancestorStateOfType(const TypeMatcher<ScrollableState>());
|
||||
}
|
||||
|
||||
/// Scrolls the closest enclosing scrollable to make the given context visible.
|
||||
|
@ -5,7 +5,7 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test/test.dart' hide TypeMatcher;
|
||||
|
||||
class TestTransition extends TransitionComponent {
|
||||
TestTransition({
|
||||
@ -99,7 +99,7 @@ void main() {
|
||||
)
|
||||
);
|
||||
|
||||
NavigatorState navigator = insideKey.currentContext.ancestorStateOfType(NavigatorState);
|
||||
NavigatorState navigator = insideKey.currentContext.ancestorStateOfType(const TypeMatcher<NavigatorState>());
|
||||
|
||||
expect(state(), equals('BC')); // transition ->1 is at 1.0
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user