Revert "Do not rebuild Routes when a new opaque Route is pushed on top (#48900)" (#49366)
This reverts commit 8eecdbe823ea09dd723a8af34c7c275e5536d728.
diff --git a/packages/flutter/lib/src/rendering/stack.dart b/packages/flutter/lib/src/rendering/stack.dart
index c72c1d1..502588b 100644
--- a/packages/flutter/lib/src/rendering/stack.dart
+++ b/packages/flutter/lib/src/rendering/stack.dart
@@ -425,8 +425,7 @@
}
}
- /// Helper function for calculating the intrinsics metrics of a Stack.
- static double getIntrinsicDimension(RenderBox firstChild, double mainChildSizeGetter(RenderBox child)) {
+ double _getIntrinsicDimension(double mainChildSizeGetter(RenderBox child)) {
double extent = 0.0;
RenderBox child = firstChild;
while (child != null) {
@@ -441,22 +440,22 @@
@override
double computeMinIntrinsicWidth(double height) {
- return getIntrinsicDimension(firstChild, (RenderBox child) => child.getMinIntrinsicWidth(height));
+ return _getIntrinsicDimension((RenderBox child) => child.getMinIntrinsicWidth(height));
}
@override
double computeMaxIntrinsicWidth(double height) {
- return getIntrinsicDimension(firstChild, (RenderBox child) => child.getMaxIntrinsicWidth(height));
+ return _getIntrinsicDimension((RenderBox child) => child.getMaxIntrinsicWidth(height));
}
@override
double computeMinIntrinsicHeight(double width) {
- return getIntrinsicDimension(firstChild, (RenderBox child) => child.getMinIntrinsicHeight(width));
+ return _getIntrinsicDimension((RenderBox child) => child.getMinIntrinsicHeight(width));
}
@override
double computeMaxIntrinsicHeight(double width) {
- return getIntrinsicDimension(firstChild, (RenderBox child) => child.getMaxIntrinsicHeight(width));
+ return _getIntrinsicDimension((RenderBox child) => child.getMaxIntrinsicHeight(width));
}
@override
@@ -464,57 +463,6 @@
return defaultComputeDistanceToHighestActualBaseline(baseline);
}
- /// Lays out the positioned `child` according to `alignment` within a Stack of `size`.
- ///
- /// Returns true when the child has visual overflow.
- static bool layoutPositionedChild(RenderBox child, StackParentData childParentData, Size size, Alignment alignment) {
- assert(childParentData.isPositioned);
- assert(child.parentData == childParentData);
-
- bool hasVisualOverflow = false;
- BoxConstraints childConstraints = const BoxConstraints();
-
- if (childParentData.left != null && childParentData.right != null)
- childConstraints = childConstraints.tighten(width: size.width - childParentData.right - childParentData.left);
- else if (childParentData.width != null)
- childConstraints = childConstraints.tighten(width: childParentData.width);
-
- if (childParentData.top != null && childParentData.bottom != null)
- childConstraints = childConstraints.tighten(height: size.height - childParentData.bottom - childParentData.top);
- else if (childParentData.height != null)
- childConstraints = childConstraints.tighten(height: childParentData.height);
-
- child.layout(childConstraints, parentUsesSize: true);
-
- double x;
- if (childParentData.left != null) {
- x = childParentData.left;
- } else if (childParentData.right != null) {
- x = size.width - childParentData.right - child.size.width;
- } else {
- x = alignment.alongOffset(size - child.size as Offset).dx;
- }
-
- if (x < 0.0 || x + child.size.width > size.width)
- hasVisualOverflow = true;
-
- double y;
- if (childParentData.top != null) {
- y = childParentData.top;
- } else if (childParentData.bottom != null) {
- y = size.height - childParentData.bottom - child.size.height;
- } else {
- y = alignment.alongOffset(size - child.size as Offset).dy;
- }
-
- if (y < 0.0 || y + child.size.height > size.height)
- hasVisualOverflow = true;
-
- childParentData.offset = Offset(x, y);
-
- return hasVisualOverflow;
- }
-
@override
void performLayout() {
_resolve();
@@ -579,7 +527,45 @@
if (!childParentData.isPositioned) {
childParentData.offset = _resolvedAlignment.alongOffset(size - child.size as Offset);
} else {
- _hasVisualOverflow = layoutPositionedChild(child, childParentData, size, _resolvedAlignment) || _hasVisualOverflow;
+ BoxConstraints childConstraints = const BoxConstraints();
+
+ if (childParentData.left != null && childParentData.right != null)
+ childConstraints = childConstraints.tighten(width: size.width - childParentData.right - childParentData.left);
+ else if (childParentData.width != null)
+ childConstraints = childConstraints.tighten(width: childParentData.width);
+
+ if (childParentData.top != null && childParentData.bottom != null)
+ childConstraints = childConstraints.tighten(height: size.height - childParentData.bottom - childParentData.top);
+ else if (childParentData.height != null)
+ childConstraints = childConstraints.tighten(height: childParentData.height);
+
+ child.layout(childConstraints, parentUsesSize: true);
+
+ double x;
+ if (childParentData.left != null) {
+ x = childParentData.left;
+ } else if (childParentData.right != null) {
+ x = size.width - childParentData.right - child.size.width;
+ } else {
+ x = _resolvedAlignment.alongOffset(size - child.size as Offset).dx;
+ }
+
+ if (x < 0.0 || x + child.size.width > size.width)
+ _hasVisualOverflow = true;
+
+ double y;
+ if (childParentData.top != null) {
+ y = childParentData.top;
+ } else if (childParentData.bottom != null) {
+ y = size.height - childParentData.bottom - child.size.height;
+ } else {
+ y = _resolvedAlignment.alongOffset(size - child.size as Offset).dy;
+ }
+
+ if (y < 0.0 || y + child.size.height > size.height)
+ _hasVisualOverflow = true;
+
+ childParentData.offset = Offset(x, y);
}
assert(child.parentData == childParentData);
diff --git a/packages/flutter/lib/src/widgets/overlay.dart b/packages/flutter/lib/src/widgets/overlay.dart
index 3ea523c..609ad2f 100644
--- a/packages/flutter/lib/src/widgets/overlay.dart
+++ b/packages/flutter/lib/src/widgets/overlay.dart
@@ -4,13 +4,13 @@
import 'dart:async';
import 'dart:collection';
-import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'basic.dart';
+import 'debug.dart';
import 'framework.dart';
import 'ticker_provider.dart';
@@ -115,7 +115,7 @@
}
OverlayState _overlay;
- final GlobalKey<_OverlayEntryWidgetState> _key = GlobalKey<_OverlayEntryWidgetState>();
+ final GlobalKey<_OverlayEntryState> _key = GlobalKey<_OverlayEntryState>();
/// Remove this entry from the overlay.
///
@@ -152,30 +152,21 @@
String toString() => '${describeIdentity(this)}(opaque: $opaque; maintainState: $maintainState)';
}
-class _OverlayEntryWidget extends StatefulWidget {
- const _OverlayEntryWidget({
- @required Key key,
- @required this.entry,
- this.tickerEnabled = true,
- }) : assert(key != null),
- assert(entry != null),
- assert(tickerEnabled != null),
- super(key: key);
+class _OverlayEntry extends StatefulWidget {
+ _OverlayEntry(this.entry)
+ : assert(entry != null),
+ super(key: entry._key);
final OverlayEntry entry;
- final bool tickerEnabled;
@override
- _OverlayEntryWidgetState createState() => _OverlayEntryWidgetState();
+ _OverlayEntryState createState() => _OverlayEntryState();
}
-class _OverlayEntryWidgetState extends State<_OverlayEntryWidget> {
+class _OverlayEntryState extends State<_OverlayEntry> {
@override
Widget build(BuildContext context) {
- return TickerMode(
- enabled: widget.tickerEnabled,
- child: widget.entry.builder(context),
- );
+ return widget.entry.builder(context);
}
void _markNeedsBuild() {
@@ -461,32 +452,28 @@
@override
Widget build(BuildContext context) {
- // This list is filled backwards and then reversed below before
- // it is added to the tree.
- final List<Widget> children = <Widget>[];
+ // These lists are filled backwards. For the offstage children that
+ // does not matter since they aren't rendered, but for the onstage
+ // children we reverse the list below before adding it to the tree.
+ final List<Widget> onstageChildren = <Widget>[];
+ final List<Widget> offstageChildren = <Widget>[];
bool onstage = true;
- int onstageCount = 0;
for (int i = _entries.length - 1; i >= 0; i -= 1) {
final OverlayEntry entry = _entries[i];
if (onstage) {
- onstageCount += 1;
- children.add(_OverlayEntryWidget(
- key: entry._key,
- entry: entry,
- ));
+ onstageChildren.add(_OverlayEntry(entry));
if (entry.opaque)
onstage = false;
} else if (entry.maintainState) {
- children.add(_OverlayEntryWidget(
- key: entry._key,
- entry: entry,
- tickerEnabled: false,
- ));
+ offstageChildren.add(TickerMode(enabled: false, child: _OverlayEntry(entry)));
}
}
return _Theatre(
- skipCount: children.length - onstageCount,
- children: children.reversed.toList(growable: false),
+ onstage: Stack(
+ fit: StackFit.expand,
+ children: onstageChildren.reversed.toList(growable: false),
+ ),
+ offstage: offstageChildren,
);
}
@@ -499,50 +486,36 @@
}
}
-/// Special version of a [Stack], that doesn't layout and render the first
-/// [skipCount] children.
+/// A widget that has one [onstage] child which is visible, and one or more
+/// [offstage] widgets which are kept alive, and are built, but are not laid out
+/// or painted.
///
-/// The first [skipCount] children are considered "offstage".
-class _Theatre extends MultiChildRenderObjectWidget {
+/// The onstage widget must be a [Stack].
+///
+/// For convenience, it is legal to use [Positioned] widgets around the offstage
+/// widgets.
+class _Theatre extends RenderObjectWidget {
_Theatre({
- Key key,
- this.skipCount = 0,
- List<Widget> children = const <Widget>[],
- }) : assert(skipCount != null),
- assert(skipCount >= 0),
- assert(children != null),
- assert(children.length >= skipCount),
- super(key: key, children: children);
+ this.onstage,
+ @required this.offstage,
+ }) : assert(offstage != null),
+ assert(!offstage.any((Widget child) => child == null));
- final int skipCount;
+ final Stack onstage;
+
+ final List<Widget> offstage;
@override
_TheatreElement createElement() => _TheatreElement(this);
@override
- _RenderTheatre createRenderObject(BuildContext context) {
- return _RenderTheatre(
- skipCount: skipCount,
- textDirection: Directionality.of(context),
- );
- }
-
- @override
- void updateRenderObject(BuildContext context, _RenderTheatre renderObject) {
- renderObject
- ..skipCount = skipCount
- ..textDirection = Directionality.of(context);
- }
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
- properties.add(IntProperty('skipCount', skipCount));
- }
+ _RenderTheatre createRenderObject(BuildContext context) => _RenderTheatre();
}
-class _TheatreElement extends MultiChildRenderObjectElement {
- _TheatreElement(_Theatre widget) : super(widget);
+class _TheatreElement extends RenderObjectElement {
+ _TheatreElement(_Theatre widget)
+ : assert(!debugChildrenHaveDuplicateKeys(widget, widget.offstage)),
+ super(widget);
@override
_Theatre get widget => super.widget as _Theatre;
@@ -550,268 +523,186 @@
@override
_RenderTheatre get renderObject => super.renderObject as _RenderTheatre;
+ Element _onstage;
+ static final Object _onstageSlot = Object();
+
+ List<Element> _offstage;
+ final Set<Element> _forgottenOffstageChildren = HashSet<Element>();
+
+ @override
+ void insertChildRenderObject(RenderBox child, dynamic slot) {
+ assert(renderObject.debugValidateChild(child));
+ if (slot == _onstageSlot) {
+ assert(child is RenderStack);
+ renderObject.child = child as RenderStack;
+ } else {
+ assert(slot == null || slot is Element);
+ renderObject.insert(child, after: slot?.renderObject as RenderBox);
+ }
+ }
+
+ @override
+ void moveChildRenderObject(RenderBox child, dynamic slot) {
+ if (slot == _onstageSlot) {
+ renderObject.remove(child);
+ assert(child is RenderStack);
+ renderObject.child = child as RenderStack;
+ } else {
+ assert(slot == null || slot is Element);
+ if (renderObject.child == child) {
+ renderObject.child = null;
+ renderObject.insert(child, after: slot?.renderObject as RenderBox);
+ } else {
+ renderObject.move(child, after: slot?.renderObject as RenderBox);
+ }
+ }
+ }
+
+ @override
+ void removeChildRenderObject(RenderBox child) {
+ if (renderObject.child == child) {
+ renderObject.child = null;
+ } else {
+ renderObject.remove(child);
+ }
+ }
+
+ @override
+ void visitChildren(ElementVisitor visitor) {
+ if (_onstage != null)
+ visitor(_onstage);
+ for (final Element child in _offstage) {
+ if (!_forgottenOffstageChildren.contains(child))
+ visitor(child);
+ }
+ }
+
@override
void debugVisitOnstageChildren(ElementVisitor visitor) {
- assert(children.length >= widget.skipCount);
- children.skip(widget.skipCount).forEach(visitor);
+ if (_onstage != null)
+ visitor(_onstage);
+ }
+
+ @override
+ bool forgetChild(Element child) {
+ if (child == _onstage) {
+ _onstage = null;
+ } else {
+ assert(_offstage.contains(child));
+ assert(!_forgottenOffstageChildren.contains(child));
+ _forgottenOffstageChildren.add(child);
+ }
+ return true;
+ }
+
+ @override
+ void mount(Element parent, dynamic newSlot) {
+ super.mount(parent, newSlot);
+ _onstage = updateChild(_onstage, widget.onstage, _onstageSlot);
+ _offstage = List<Element>(widget.offstage.length);
+ Element previousChild;
+ for (int i = 0; i < _offstage.length; i += 1) {
+ final Element newChild = inflateWidget(widget.offstage[i], previousChild);
+ _offstage[i] = newChild;
+ previousChild = newChild;
+ }
+ }
+
+ @override
+ void update(_Theatre newWidget) {
+ super.update(newWidget);
+ assert(widget == newWidget);
+ _onstage = updateChild(_onstage, widget.onstage, _onstageSlot);
+ _offstage = updateChildren(_offstage, widget.offstage, forgottenChildren: _forgottenOffstageChildren);
+ _forgottenOffstageChildren.clear();
}
}
-class _RenderTheatre extends RenderBox with ContainerRenderObjectMixin<RenderBox, StackParentData> {
- _RenderTheatre({
- List<RenderBox> children,
- @required TextDirection textDirection,
- int skipCount = 0,
- }) : assert(skipCount != null),
- assert(skipCount >= 0),
- assert(textDirection != null),
- _textDirection = textDirection,
- _skipCount = skipCount {
- addAll(children);
- }
-
- bool _hasVisualOverflow = false;
+// A render object which lays out and paints one subtree while keeping a list
+// of other subtrees alive but not laid out or painted (the "zombie" children).
+//
+// The subtree that is laid out and painted must be a [RenderStack].
+//
+// This class uses [StackParentData] objects for its parent data so that the
+// children of its primary subtree's stack can be moved to this object's list
+// of zombie children without changing their parent data objects.
+class _RenderTheatre extends RenderBox
+ with RenderObjectWithChildMixin<RenderStack>, RenderProxyBoxMixin<RenderStack>,
+ ContainerRenderObjectMixin<RenderBox, StackParentData> {
@override
- void setupParentData(RenderBox child) {
+ void setupParentData(RenderObject child) {
if (child.parentData is! StackParentData)
child.parentData = StackParentData();
}
- Alignment _resolvedAlignment;
-
- void _resolve() {
- if (_resolvedAlignment != null)
- return;
- _resolvedAlignment = AlignmentDirectional.topStart.resolve(textDirection);
- }
-
- void _markNeedResolution() {
- _resolvedAlignment = null;
- markNeedsLayout();
- }
-
- TextDirection get textDirection => _textDirection;
- TextDirection _textDirection;
- set textDirection(TextDirection value) {
- if (_textDirection == value)
- return;
- _textDirection = value;
- _markNeedResolution();
- }
-
- int get skipCount => _skipCount;
- int _skipCount;
- set skipCount(int value) {
- assert(value != null);
- if (_skipCount != value) {
- _skipCount = value;
- markNeedsLayout();
- }
- }
-
- RenderBox get _firstOnstageChild {
- if (skipCount == super.childCount) {
- return null;
- }
- RenderBox child = super.firstChild;
- for (int toSkip = skipCount; toSkip > 0; toSkip--) {
- final StackParentData childParentData = child.parentData as StackParentData;
- child = childParentData.nextSibling;
- assert(child != null);
- }
- return child;
- }
-
- RenderBox get _lastOnstageChild => skipCount == super.childCount ? null : lastChild;
-
- int get _onstageChildCount => childCount - skipCount;
+ // Because both RenderObjectWithChildMixin and ContainerRenderObjectMixin
+ // define redepthChildren, visitChildren and debugDescribeChildren and don't
+ // call super, we have to define them again here to make sure the work of both
+ // is done.
+ //
+ // We chose to put ContainerRenderObjectMixin last in the inheritance chain so
+ // that we can call super to hit its more complex definitions of
+ // redepthChildren and visitChildren, and then duplicate the more trivial
+ // definition from RenderObjectWithChildMixin inline in our version here.
+ //
+ // This code duplication is suboptimal.
+ // TODO(ianh): Replace this with a better solution once https://github.com/dart-lang/sdk/issues/27100 is fixed
+ //
+ // For debugDescribeChildren we just roll our own because otherwise the line
+ // drawings won't really work as well.
@override
- double computeMinIntrinsicWidth(double height) {
- return RenderStack.getIntrinsicDimension(_firstOnstageChild, (RenderBox child) => child.getMinIntrinsicWidth(height));
+ void redepthChildren() {
+ if (child != null)
+ redepthChild(child);
+ super.redepthChildren();
}
@override
- double computeMaxIntrinsicWidth(double height) {
- return RenderStack.getIntrinsicDimension(_firstOnstageChild, (RenderBox child) => child.getMaxIntrinsicWidth(height));
- }
-
- @override
- double computeMinIntrinsicHeight(double width) {
- return RenderStack.getIntrinsicDimension(_firstOnstageChild, (RenderBox child) => child.getMinIntrinsicHeight(width));
- }
-
- @override
- double computeMaxIntrinsicHeight(double width) {
- return RenderStack.getIntrinsicDimension(_firstOnstageChild, (RenderBox child) => child.getMaxIntrinsicHeight(width));
- }
-
- @override
- double computeDistanceToActualBaseline(TextBaseline baseline) {
- assert(!debugNeedsLayout);
- double result;
- RenderBox child = _firstOnstageChild;
- while (child != null) {
- assert(!child.debugNeedsLayout);
- final StackParentData childParentData = child.parentData as StackParentData;
- double candidate = child.getDistanceToActualBaseline(baseline);
- if (candidate != null) {
- candidate += childParentData.offset.dy;
- if (result != null) {
- result = math.min(result, candidate);
- } else {
- result = candidate;
- }
- }
- child = childParentData.nextSibling;
- }
- return result;
- }
-
- @override
- bool get sizedByParent => true;
-
- @override
- void performResize() {
- size = constraints.biggest;
- assert(size.isFinite);
- }
-
- @override
- void performLayout() {
- _hasVisualOverflow = false;
-
- if (_onstageChildCount == 0) {
- return;
- }
-
- _resolve();
- assert(_resolvedAlignment != null);
-
- // Same BoxConstraints as used by RenderStack for StackFit.expand.
- final BoxConstraints nonPositionedConstraints = BoxConstraints.tight(constraints.biggest);
-
- RenderBox child = _firstOnstageChild;
- while (child != null) {
- final StackParentData childParentData = child.parentData as StackParentData;
-
- if (!childParentData.isPositioned) {
- child.layout(nonPositionedConstraints, parentUsesSize: true);
- childParentData.offset = _resolvedAlignment.alongOffset(size - child.size as Offset);
- } else {
- _hasVisualOverflow = RenderStack.layoutPositionedChild(child, childParentData, size, _resolvedAlignment) || _hasVisualOverflow;
- }
-
- assert(child.parentData == childParentData);
- child = childParentData.nextSibling;
- }
- }
-
- @override
- bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
- RenderBox child = _lastOnstageChild;
- for (int i = 0; i < _onstageChildCount; i++) {
- assert(child != null);
- final StackParentData childParentData = child.parentData as StackParentData;
- final bool isHit = result.addWithPaintOffset(
- offset: childParentData.offset,
- position: position,
- hitTest: (BoxHitTestResult result, Offset transformed) {
- assert(transformed == position - childParentData.offset);
- return child.hitTest(result, position: transformed);
- },
- );
- if (isHit)
- return true;
- child = childParentData.previousSibling;
- }
- return false;
- }
-
- @protected
- void paintStack(PaintingContext context, Offset offset) {
- RenderBox child = _firstOnstageChild;
- while (child != null) {
- final StackParentData childParentData = child.parentData as StackParentData;
- context.paintChild(child, childParentData.offset + offset);
- child = childParentData.nextSibling;
- }
- }
-
- @override
- void paint(PaintingContext context, Offset offset) {
- if (_hasVisualOverflow) {
- context.pushClipRect(needsCompositing, offset, Offset.zero & size, paintStack);
- } else {
- paintStack(context, offset);
- }
- }
-
- @override
- void visitChildrenForSemantics(RenderObjectVisitor visitor) {
- RenderBox child = _firstOnstageChild;
- while (child != null) {
+ void visitChildren(RenderObjectVisitor visitor) {
+ if (child != null)
visitor(child);
- final StackParentData childParentData = child.parentData as StackParentData;
- child = childParentData.nextSibling;
- }
- }
-
- @override
- Rect describeApproximatePaintClip(RenderObject child) => _hasVisualOverflow ? Offset.zero & size : null;
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
- properties.add(IntProperty('skipCount', skipCount));
- properties.add(EnumProperty<TextDirection>('textDirection', textDirection));
+ super.visitChildren(visitor);
}
@override
List<DiagnosticsNode> debugDescribeChildren() {
- final List<DiagnosticsNode> offstageChildren = <DiagnosticsNode>[];
- final List<DiagnosticsNode> onstageChildren = <DiagnosticsNode>[];
+ final List<DiagnosticsNode> children = <DiagnosticsNode>[
+ if (child != null) child.toDiagnosticsNode(name: 'onstage'),
+ ];
- int count = 1;
- bool onstage = false;
- RenderBox child = firstChild;
- final RenderBox firstOnstageChild = _firstOnstageChild;
- while (child != null) {
- if (child == firstOnstageChild) {
- onstage = true;
- count = 1;
- }
+ if (firstChild != null) {
+ RenderBox child = firstChild;
- if (onstage) {
- onstageChildren.add(
- child.toDiagnosticsNode(
- name: 'onstage $count',
- ),
- );
- } else {
- offstageChildren.add(
+ int count = 1;
+ while (true) {
+ children.add(
child.toDiagnosticsNode(
name: 'offstage $count',
style: DiagnosticsTreeStyle.offstage,
),
);
+ if (child == lastChild)
+ break;
+ final StackParentData childParentData = child.parentData as StackParentData;
+ child = childParentData.nextSibling;
+ count += 1;
}
-
- final StackParentData childParentData = child.parentData as StackParentData;
- child = childParentData.nextSibling;
- count += 1;
- }
-
- return <DiagnosticsNode>[
- ...onstageChildren,
- if (offstageChildren.isNotEmpty)
- ...offstageChildren
- else
+ } else {
+ children.add(
DiagnosticsNode.message(
'no offstage children',
style: DiagnosticsTreeStyle.offstage,
),
- ];
+ );
+ }
+ return children;
+ }
+
+ @override
+ void visitChildrenForSemantics(RenderObjectVisitor visitor) {
+ if (child != null)
+ visitor(child);
}
}
diff --git a/packages/flutter/test/cupertino/nav_bar_transition_test.dart b/packages/flutter/test/cupertino/nav_bar_transition_test.dart
index f65d4b0..dda551e 100644
--- a/packages/flutter/test/cupertino/nav_bar_transition_test.dart
+++ b/packages/flutter/test/cupertino/nav_bar_transition_test.dart
@@ -72,9 +72,12 @@
}
Finder flying(WidgetTester tester, Finder finder) {
- final ContainerRenderObjectMixin<RenderBox, StackParentData> theater = tester.renderObject(find.byType(Overlay));
+ final RenderObjectWithChildMixin<RenderStack> theater =
+ tester.renderObject(find.byType(Overlay));
+ final RenderStack theaterStack = theater.child;
final Finder lastOverlayFinder = find.byElementPredicate((Element element) {
- return element is RenderObjectElement && element.renderObject == theater.lastChild;
+ return element is RenderObjectElement &&
+ element.renderObject == theaterStack.lastChild;
});
assert(
diff --git a/packages/flutter/test/material/debug_test.dart b/packages/flutter/test/material/debug_test.dart
index 8ab0c9a..0874371 100644
--- a/packages/flutter/test/material/debug_test.dart
+++ b/packages/flutter/test/material/debug_test.dart
@@ -132,8 +132,8 @@
' Offstage\n'
' _ModalScopeStatus\n'
' _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#969b7]\n'
- ' TickerMode\n'
- ' _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#545d0]\n'
+ ' _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#7a3ae]\n'
+ ' Stack\n'
' _Theatre\n'
' Overlay-[LabeledGlobalKey<OverlayState>#31a52]\n'
' _FocusMarker\n'
diff --git a/packages/flutter/test/material/stepper_test.dart b/packages/flutter/test/material/stepper_test.dart
index bd6266b..13ee688 100644
--- a/packages/flutter/test/material/stepper_test.dart
+++ b/packages/flutter/test/material/stepper_test.dart
@@ -529,13 +529,12 @@
// which will change depending on where the test is run.
expect(lines.length, greaterThan(7));
expect(
- lines.take(9).join('\n'),
+ lines.take(8).join('\n'),
equalsIgnoringHashCodes(
'══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞════════════════════════\n'
'The following assertion was thrown building Stepper(dirty,\n'
- 'dependencies: [TickerMode,\n'
- '_LocalizationsScope-[GlobalKey#6b31b]], state:\n'
- '_StepperState#1bf00):\n'
+ 'dependencies: [_LocalizationsScope-[GlobalKey#00000]], state:\n'
+ '_StepperState#00000):\n'
'Steppers must not be nested.\n'
'The material specification advises that one should avoid\n'
'embedding steppers within steppers.\n'
diff --git a/packages/flutter/test/widgets/navigator_test.dart b/packages/flutter/test/widgets/navigator_test.dart
index aace92d..5522c11 100644
--- a/packages/flutter/test/widgets/navigator_test.dart
+++ b/packages/flutter/test/widgets/navigator_test.dart
@@ -1186,33 +1186,6 @@
expect(find.byKey(const ValueKey<String>('/A/B')), findsNothing); // popped
expect(find.byKey(const ValueKey<String>('/C')), findsOneWidget);
});
-
- testWidgets('Pushing opaque Route does not rebuild routes below', (WidgetTester tester) async {
- // Regression test for https://github.com/flutter/flutter/issues/45797.
-
- final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
- final Key bottomRoute = UniqueKey();
- final Key topRoute = UniqueKey();
- await tester.pumpWidget(
- MaterialApp(
- navigatorKey: navigator,
- routes: <String, WidgetBuilder>{
- '/' : (BuildContext context) => StatefulTestWidget(key: bottomRoute),
- '/a': (BuildContext context) => StatefulTestWidget(key: topRoute),
- },
- ),
- );
- expect(tester.state<StatefulTestState>(find.byKey(bottomRoute)).rebuildCount, 1);
-
- navigator.currentState.pushNamed('/a');
- await tester.pumpAndSettle();
-
- // Bottom route is offstage and did not rebuild.
- expect(find.byKey(bottomRoute), findsNothing);
- expect(tester.state<StatefulTestState>(find.byKey(bottomRoute, skipOffstage: false)).rebuildCount, 1);
-
- expect(tester.state<StatefulTestState>(find.byKey(topRoute)).rebuildCount, 1);
- });
}
class NoAnimationPageRoute extends PageRouteBuilder<void> {
@@ -1226,20 +1199,3 @@
return super.createAnimationController()..value = 1.0;
}
}
-
-class StatefulTestWidget extends StatefulWidget {
- const StatefulTestWidget({Key key}) : super(key: key);
-
- @override
- State<StatefulTestWidget> createState() => StatefulTestState();
-}
-
-class StatefulTestState extends State<StatefulTestWidget> {
- int rebuildCount = 0;
-
- @override
- Widget build(BuildContext context) {
- rebuildCount += 1;
- return Container();
- }
-}
diff --git a/packages/flutter/test/widgets/overlay_test.dart b/packages/flutter/test/widgets/overlay_test.dart
index 21b60d8..0453fcd 100644
--- a/packages/flutter/test/widgets/overlay_test.dart
+++ b/packages/flutter/test/widgets/overlay_test.dart
@@ -6,8 +6,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
-import 'semantics_tester.dart';
-
void main() {
testWidgets('OverflowEntries context contains Overlay', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
@@ -27,9 +25,6 @@
return Container();
},
),
- OverlayEntry(
- builder: (BuildContext context) => Container(),
- )
],
),
),
@@ -41,42 +36,36 @@
expect(
theater.toStringDeep(minLevel: DiagnosticLevel.info),
equalsIgnoringHashCodes(
- '_RenderTheatre#744c9\n'
- ' │ parentData: <none>\n'
- ' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
- ' │ size: Size(800.0, 600.0)\n'
- ' │ skipCount: 0\n'
- ' │ textDirection: ltr\n'
- ' │\n'
- ' ├─onstage 1: RenderLimitedBox#bb803\n'
- ' │ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use\n'
- ' │ │ size)\n'
- ' │ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
- ' │ │ size: Size(800.0, 600.0)\n'
- ' │ │ maxWidth: 0.0\n'
- ' │ │ maxHeight: 0.0\n'
- ' │ │\n'
- ' │ └─child: RenderConstrainedBox#62707\n'
- ' │ parentData: <none> (can use size)\n'
- ' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
- ' │ size: Size(800.0, 600.0)\n'
- ' │ additionalConstraints: BoxConstraints(biggest)\n'
- ' │\n'
- ' ├─onstage 2: RenderLimitedBox#af5f1\n'
- ' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use\n'
- ' ╎ │ size)\n'
- ' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
- ' ╎ │ size: Size(800.0, 600.0)\n'
- ' ╎ │ maxWidth: 0.0\n'
- ' ╎ │ maxHeight: 0.0\n'
- ' ╎ │\n'
- ' ╎ └─child: RenderConstrainedBox#69c48\n'
- ' ╎ parentData: <none> (can use size)\n'
- ' ╎ constraints: BoxConstraints(w=800.0, h=600.0)\n'
- ' ╎ size: Size(800.0, 600.0)\n'
- ' ╎ additionalConstraints: BoxConstraints(biggest)\n'
- ' ╎\n'
- ' └╌no offstage children\n'
+ '_RenderTheatre#f5cf2\n'
+ ' │ parentData: <none>\n'
+ ' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' │ size: Size(800.0, 600.0)\n'
+ ' │\n'
+ ' ├─onstage: RenderStack#39819\n'
+ ' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use\n'
+ ' ╎ │ size)\n'
+ ' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' ╎ │ size: Size(800.0, 600.0)\n'
+ ' ╎ │ alignment: AlignmentDirectional.topStart\n'
+ ' ╎ │ textDirection: ltr\n'
+ ' ╎ │ fit: expand\n'
+ ' ╎ │ overflow: clip\n'
+ ' ╎ │\n'
+ ' ╎ └─child 1: RenderLimitedBox#d1448\n'
+ ' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use\n'
+ ' ╎ │ size)\n'
+ ' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' ╎ │ size: Size(800.0, 600.0)\n'
+ ' ╎ │ maxWidth: 0.0\n'
+ ' ╎ │ maxHeight: 0.0\n'
+ ' ╎ │\n'
+ ' ╎ └─child: RenderConstrainedBox#e8b87\n'
+ ' ╎ parentData: <none> (can use size)\n'
+ ' ╎ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' ╎ size: Size(800.0, 600.0)\n'
+ ' ╎ additionalConstraints: BoxConstraints(biggest)\n'
+ ' ╎\n'
+ ' └╌no offstage children\n'
),
);
});
@@ -114,52 +103,60 @@
expect(
theater.toStringDeep(minLevel: DiagnosticLevel.info),
equalsIgnoringHashCodes(
- '_RenderTheatre#385b3\n'
+ '_RenderTheatre#b22a8\n'
' │ parentData: <none>\n'
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
' │ size: Size(800.0, 600.0)\n'
- ' │ skipCount: 2\n'
- ' │ textDirection: ltr\n'
' │\n'
- ' ├─onstage 1: RenderLimitedBox#0a77a\n'
+ ' ├─onstage: RenderStack#eab87\n'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use\n'
' ╎ │ size)\n'
' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
' ╎ │ size: Size(800.0, 600.0)\n'
- ' ╎ │ maxWidth: 0.0\n'
- ' ╎ │ maxHeight: 0.0\n'
+ ' ╎ │ alignment: AlignmentDirectional.topStart\n'
+ ' ╎ │ textDirection: ltr\n'
+ ' ╎ │ fit: expand\n'
+ ' ╎ │ overflow: clip\n'
' ╎ │\n'
- ' ╎ └─child: RenderConstrainedBox#21f3a\n'
- ' ╎ parentData: <none> (can use size)\n'
- ' ╎ constraints: BoxConstraints(w=800.0, h=600.0)\n'
- ' ╎ size: Size(800.0, 600.0)\n'
- ' ╎ additionalConstraints: BoxConstraints(biggest)\n'
+ ' ╎ └─child 1: RenderLimitedBox#ca15b\n'
+ ' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use\n'
+ ' ╎ │ size)\n'
+ ' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' ╎ │ size: Size(800.0, 600.0)\n'
+ ' ╎ │ maxWidth: 0.0\n'
+ ' ╎ │ maxHeight: 0.0\n'
+ ' ╎ │\n'
+ ' ╎ └─child: RenderConstrainedBox#dffe5\n'
+ ' ╎ parentData: <none> (can use size)\n'
+ ' ╎ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' ╎ size: Size(800.0, 600.0)\n'
+ ' ╎ additionalConstraints: BoxConstraints(biggest)\n'
' ╎\n'
- ' ╎╌offstage 1: RenderLimitedBox#62c8c NEEDS-LAYOUT NEEDS-PAINT\n'
+ ' ╎╌offstage 1: RenderLimitedBox#b6f09 NEEDS-LAYOUT NEEDS-PAINT\n'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0)\n'
' ╎ │ constraints: MISSING\n'
' ╎ │ size: MISSING\n'
' ╎ │ maxWidth: 0.0\n'
' ╎ │ maxHeight: 0.0\n'
' ╎ │\n'
- ' ╎ └─child: RenderConstrainedBox#425fa NEEDS-LAYOUT NEEDS-PAINT\n'
+ ' ╎ └─child: RenderConstrainedBox#5a057 NEEDS-LAYOUT NEEDS-PAINT\n'
' ╎ parentData: <none>\n'
' ╎ constraints: MISSING\n'
' ╎ size: MISSING\n'
' ╎ additionalConstraints: BoxConstraints(biggest)\n'
' ╎\n'
- ' └╌offstage 2: RenderLimitedBox#03ae2 NEEDS-LAYOUT NEEDS-PAINT\n'
+ ' └╌offstage 2: RenderLimitedBox#f689e NEEDS-LAYOUT NEEDS-PAINT\n'
' │ parentData: not positioned; offset=Offset(0.0, 0.0)\n'
' │ constraints: MISSING\n'
' │ size: MISSING\n'
' │ maxWidth: 0.0\n'
' │ maxHeight: 0.0\n'
' │\n'
- ' └─child: RenderConstrainedBox#b4d48 NEEDS-LAYOUT NEEDS-PAINT\n'
+ ' └─child: RenderConstrainedBox#c15f0 NEEDS-LAYOUT NEEDS-PAINT\n'
' parentData: <none>\n'
' constraints: MISSING\n'
' size: MISSING\n'
- ' additionalConstraints: BoxConstraints(biggest)\n',
+ ' additionalConstraints: BoxConstraints(biggest)\n'
),
);
});
@@ -701,261 +698,4 @@
expect(find.byKey(root), findsNothing);
expect(find.byKey(top), findsOneWidget);
});
-
- testWidgets('OverlayEntries do not rebuild when opaqueness changes', (WidgetTester tester) async {
- // Regression test for https://github.com/flutter/flutter/issues/45797.
-
- final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
- final Key bottom = UniqueKey();
- final Key middle = UniqueKey();
- final Key top = UniqueKey();
- final Widget bottomWidget = StatefulTestWidget(key: bottom);
- final Widget middleWidget = StatefulTestWidget(key: middle);
- final Widget topWidget = StatefulTestWidget(key: top);
-
- final OverlayEntry bottomEntry = OverlayEntry(
- maintainState: true,
- builder: (BuildContext context) {
- return bottomWidget;
- },
- );
- final OverlayEntry middleEntry = OverlayEntry(
- maintainState: true,
- builder: (BuildContext context) {
- return middleWidget;
- },
- );
- final OverlayEntry topEntry = OverlayEntry(
- maintainState: true,
- builder: (BuildContext context) {
- return topWidget;
- },
- );
-
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: Overlay(
- key: overlayKey,
- initialEntries: <OverlayEntry>[
- bottomEntry,
- middleEntry,
- topEntry,
- ],
- ),
- ),
- );
-
- // All widgets are onstage.
- expect(tester.state<StatefulTestState>(find.byKey(bottom)).rebuildCount, 1);
- expect(tester.state<StatefulTestState>(find.byKey(middle)).rebuildCount, 1);
- expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
-
- middleEntry.opaque = true;
- await tester.pump();
-
- // Bottom widget is offstage and did not rebuild.
- expect(find.byKey(bottom), findsNothing);
- expect(tester.state<StatefulTestState>(find.byKey(bottom, skipOffstage: false)).rebuildCount, 1);
- expect(tester.state<StatefulTestState>(find.byKey(middle)).rebuildCount, 1);
- expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
- });
-
- testWidgets('OverlayEntries do not rebuild when opaque entry is added', (WidgetTester tester) async {
- // Regression test for https://github.com/flutter/flutter/issues/45797.
-
- final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
- final Key bottom = UniqueKey();
- final Key middle = UniqueKey();
- final Key top = UniqueKey();
- final Widget bottomWidget = StatefulTestWidget(key: bottom);
- final Widget middleWidget = StatefulTestWidget(key: middle);
- final Widget topWidget = StatefulTestWidget(key: top);
-
- final OverlayEntry bottomEntry = OverlayEntry(
- maintainState: true,
- builder: (BuildContext context) {
- return bottomWidget;
- },
- );
- final OverlayEntry middleEntry = OverlayEntry(
- opaque: true,
- maintainState: true,
- builder: (BuildContext context) {
- return middleWidget;
- },
- );
- final OverlayEntry topEntry = OverlayEntry(
- maintainState: true,
- builder: (BuildContext context) {
- return topWidget;
- },
- );
-
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: Overlay(
- key: overlayKey,
- initialEntries: <OverlayEntry>[
- bottomEntry,
- topEntry,
- ],
- ),
- ),
- );
-
- // Both widgets are onstage.
- expect(tester.state<StatefulTestState>(find.byKey(bottom)).rebuildCount, 1);
- expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
-
- overlayKey.currentState.rearrange(<OverlayEntry>[
- bottomEntry, middleEntry, topEntry,
- ]);
- await tester.pump();
-
- // Bottom widget is offstage and did not rebuild.
- expect(find.byKey(bottom), findsNothing);
- expect(tester.state<StatefulTestState>(find.byKey(bottom, skipOffstage: false)).rebuildCount, 1);
- expect(tester.state<StatefulTestState>(find.byKey(middle)).rebuildCount, 1);
- expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
- });
-
- testWidgets('entries below opaque entries are ignored for hit testing', (WidgetTester tester) async {
- final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
- int bottomTapCount = 0;
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: Overlay(
- key: overlayKey,
- initialEntries: <OverlayEntry>[
- OverlayEntry(
- maintainState: true,
- builder: (BuildContext context) {
- return GestureDetector(
- onTap: () {
- bottomTapCount++;
- },
- );
- },
- ),
- ],
- ),
- ),
- );
-
- expect(bottomTapCount, 0);
- await tester.tap(find.byKey(overlayKey));
- expect(bottomTapCount, 1);
-
- overlayKey.currentState.insert(OverlayEntry(
- maintainState: true,
- opaque: true,
- builder: (BuildContext context) {
- return Container();
- },
- ));
- await tester.pump();
-
- // Bottom is offstage and does not receive tap events.
- expect(find.byType(GestureDetector), findsNothing);
- expect(find.byType(GestureDetector, skipOffstage: false), findsOneWidget);
- await tester.tap(find.byKey(overlayKey));
- expect(bottomTapCount, 1);
-
- int topTapCount = 0;
- overlayKey.currentState.insert(OverlayEntry(
- maintainState: true,
- opaque: true,
- builder: (BuildContext context) {
- return GestureDetector(
- onTap: () {
- topTapCount++;
- },
- );
- },
- ));
- await tester.pump();
-
- expect(topTapCount, 0);
- await tester.tap(find.byKey(overlayKey));
- expect(topTapCount, 1);
- expect(bottomTapCount, 1);
- });
-
- testWidgets('Semantics of entries below opaque entries are ignored', (WidgetTester tester) async {
- final SemanticsTester semantics = SemanticsTester(tester);
- final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: Overlay(
- key: overlayKey,
- initialEntries: <OverlayEntry>[
- OverlayEntry(
- maintainState: true,
- builder: (BuildContext context) {
- return const Text('bottom');
- },
- ),
- OverlayEntry(
- maintainState: true,
- opaque: true,
- builder: (BuildContext context) {
- return const Text('top');
- },
- ),
- ],
- ),
- ),
- );
- expect(find.text('bottom'), findsNothing);
- expect(find.text('bottom', skipOffstage: false), findsOneWidget);
- expect(find.text('top'), findsOneWidget);
- expect(semantics, includesNodeWith(label: 'top'));
- expect(semantics, isNot(includesNodeWith(label: 'bottom')));
-
- semantics.dispose();
- });
-
- testWidgets('Can used Positioned within OverlayEntry', (WidgetTester tester) async {
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: Overlay(
- initialEntries: <OverlayEntry>[
- OverlayEntry(
- builder: (BuildContext context) {
- return const Positioned(
- left: 145,
- top: 123,
- child: Text('positioned child'),
- );
- },
- ),
- ],
- ),
- ),
- );
-
- expect(tester.getTopLeft(find.text('positioned child')), const Offset(145, 123));
- });
-}
-
-class StatefulTestWidget extends StatefulWidget {
- const StatefulTestWidget({Key key}) : super(key: key);
-
- @override
- State<StatefulTestWidget> createState() => StatefulTestState();
-}
-
-class StatefulTestState extends State<StatefulTestWidget> {
- int rebuildCount = 0;
-
- @override
- Widget build(BuildContext context) {
- rebuildCount += 1;
- return Container();
- }
}