Commit d55e5a69 authored by Ian Hickson's avatar Ian Hickson

Merge pull request #2445 from Hixie/overlay

Clarify the "needs an Overlay" assert.
parents 93a0fb44 1abc7c9e
...@@ -182,6 +182,7 @@ class _DropDownRoute<T> extends PopupRoute<_DropDownRouteResult<T>> { ...@@ -182,6 +182,7 @@ class _DropDownRoute<T> extends PopupRoute<_DropDownRouteResult<T>> {
ModalPosition getPosition(BuildContext context) { ModalPosition getPosition(BuildContext context) {
RenderBox overlayBox = Overlay.of(context).context.findRenderObject(); RenderBox overlayBox = Overlay.of(context).context.findRenderObject();
assert(overlayBox != null); // can't be null; routes get inserted by Navigator which has its own Overlay
Size overlaySize = overlayBox.size; Size overlaySize = overlayBox.size;
RelativeRect menuRect = new RelativeRect.fromSize(rect, overlaySize); RelativeRect menuRect = new RelativeRect.fromSize(rect, overlaySize);
return new ModalPosition( return new ModalPosition(
......
...@@ -46,6 +46,7 @@ class Tooltip extends StatefulComponent { ...@@ -46,6 +46,7 @@ class Tooltip extends StatefulComponent {
assert(preferBelow != null); assert(preferBelow != null);
assert(fadeDuration != null); assert(fadeDuration != null);
assert(showDuration != null); assert(showDuration != null);
assert(child != null);
} }
final String message; final String message;
...@@ -150,7 +151,7 @@ class _TooltipState extends State<Tooltip> { ...@@ -150,7 +151,7 @@ class _TooltipState extends State<Tooltip> {
preferBelow: config.preferBelow preferBelow: config.preferBelow
); );
}); });
Overlay.of(context).insert(_entry); Overlay.of(context, debugRequiredFor: config).insert(_entry);
} }
_timer?.cancel(); _timer?.cancel();
if (_controller.status != AnimationStatus.completed) { if (_controller.status != AnimationStatus.completed) {
...@@ -175,7 +176,7 @@ class _TooltipState extends State<Tooltip> { ...@@ -175,7 +176,7 @@ class _TooltipState extends State<Tooltip> {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(Overlay.of(context) != null); assert(Overlay.of(context, debugRequiredFor: config) != null);
return new GestureDetector( return new GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onLongPress: showTooltip, onLongPress: showTooltip,
......
...@@ -234,7 +234,7 @@ class _DraggableState<T> extends State<DraggableBase<T>> { ...@@ -234,7 +234,7 @@ class _DraggableState<T> extends State<DraggableBase<T>> {
_activeCount += 1; _activeCount += 1;
}); });
return new _DragAvatar<T>( return new _DragAvatar<T>(
overlay: Overlay.of(context), overlay: Overlay.of(context, debugRequiredFor: config),
data: config.data, data: config.data,
initialPosition: position, initialPosition: position,
dragStartPoint: dragStartPoint, dragStartPoint: dragStartPoint,
...@@ -249,6 +249,7 @@ class _DraggableState<T> extends State<DraggableBase<T>> { ...@@ -249,6 +249,7 @@ class _DraggableState<T> extends State<DraggableBase<T>> {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(Overlay.of(context, debugRequiredFor: config) != null);
final bool canDrag = config.maxSimultaneousDrags == null || final bool canDrag = config.maxSimultaneousDrags == null ||
_activeCount < config.maxSimultaneousDrags; _activeCount < config.maxSimultaneousDrags;
final bool showChild = _activeCount == 0 || config.childWhenDragging == null; final bool showChild = _activeCount == 0 || config.childWhenDragging == null;
......
...@@ -137,6 +137,9 @@ class Mimic extends StatelessComponent { ...@@ -137,6 +137,9 @@ class Mimic extends StatelessComponent {
} }
/// A widget that can be copied by a [Mimic]. /// A widget that can be copied by a [Mimic].
///
/// This widget's State, [MimicableState], contains an API for initiating the
/// mimic operation.
class Mimicable extends StatefulComponent { class Mimicable extends StatefulComponent {
Mimicable({ Key key, this.child }) : super(key: key); Mimicable({ Key key, this.child }) : super(key: key);
...@@ -193,8 +196,7 @@ class MimicableState extends State<Mimicable> { ...@@ -193,8 +196,7 @@ class MimicableState extends State<Mimicable> {
/// had when the mimicking process started and (2) the child will be /// had when the mimicking process started and (2) the child will be
/// placed in the enclosing overlay. /// placed in the enclosing overlay.
MimicOverlayEntry liftToOverlay() { MimicOverlayEntry liftToOverlay() {
OverlayState overlay = Overlay.of(context); OverlayState overlay = Overlay.of(context, debugRequiredFor: config);
assert(overlay != null); // You need an overlay to lift into.
MimicOverlayEntry entry = new MimicOverlayEntry._(startMimic()); MimicOverlayEntry entry = new MimicOverlayEntry._(startMimic());
overlay.insert(entry._overlayEntry); overlay.insert(entry._overlayEntry);
return entry; return entry;
......
...@@ -71,7 +71,32 @@ class Overlay extends StatefulComponent { ...@@ -71,7 +71,32 @@ class Overlay extends StatefulComponent {
final List<OverlayEntry> initialEntries; final List<OverlayEntry> initialEntries;
/// The state from the closest instance of this class that encloses the given context. /// The state from the closest instance of this class that encloses the given context.
static OverlayState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<OverlayState>()); ///
/// In checked mode, if the [debugRequiredFor] argument is provided then this
/// function will assert that an overlay was found and will throw an exception
/// if not. The exception attempts to explain that the calling [Widget] (the
/// one given by the [debugRequiredFor] argument) needs an [Overlay] to be
/// present to function.
static OverlayState of(BuildContext context, { Widget debugRequiredFor }) {
OverlayState result = context.ancestorStateOfType(const TypeMatcher<OverlayState>());
assert(() {
if (debugRequiredFor != null && result == null) {
String additional = context.widget != debugRequiredFor
? '\nThe context from which that widget was searching for an overlay was:\n $context'
: '';
throw new WidgetError(
'No Overlay widget found.\n'
'${debugRequiredFor.runtimeType} widgets require an Overlay widget ancestor for correct operation.\n'
'The most common way to add an Overlay to an application is to include a MaterialApp or Navigator widget in the runApp() call.\n'
'The specific widget that failed to find an overlay was:\n'
' $debugRequiredFor'
'$additional'
);
}
return true;
});
return result;
}
OverlayState createState() => new OverlayState(); OverlayState createState() => new OverlayState();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment