Commit fd119f4f authored by Dragoș Tiselice's avatar Dragoș Tiselice Committed by GitHub

Added clipping opt-out for Stack. (#5326)

Added a flag that instructs Stack how to deal with overflowing
children: they can either be clipped or not.
parent 9c15407b
...@@ -199,6 +199,15 @@ class StackParentData extends ContainerBoxParentDataMixin<RenderBox> { ...@@ -199,6 +199,15 @@ class StackParentData extends ContainerBoxParentDataMixin<RenderBox> {
} }
} }
/// Whether overflowing children should be clipped, or their overflows be
/// visible.
enum Overflow {
/// Children's overflows will be visible.
visible,
/// Children's overflows will be clipped.
clip
}
/// Implements the stack layout algorithm /// Implements the stack layout algorithm
/// ///
/// In a stack layout, the children are positioned on top of each other in the /// In a stack layout, the children are positioned on top of each other in the
...@@ -244,8 +253,11 @@ class RenderStack extends RenderBox ...@@ -244,8 +253,11 @@ class RenderStack extends RenderBox
/// top left corners. /// top left corners.
RenderStack({ RenderStack({
List<RenderBox> children, List<RenderBox> children,
FractionalOffset alignment: FractionalOffset.topLeft FractionalOffset alignment: FractionalOffset.topLeft,
}) : _alignment = alignment { Overflow overflow: Overflow.clip
}) : _alignment = alignment,
_overflow = overflow {
assert(overflow != null);
addAll(children); addAll(children);
} }
...@@ -257,6 +269,20 @@ class RenderStack extends RenderBox ...@@ -257,6 +269,20 @@ class RenderStack extends RenderBox
child.parentData = new StackParentData(); child.parentData = new StackParentData();
} }
/// Whether overflowing children should be clipped. See [Overflow].
///
/// Some children in a stack might overflow its box. When this flag is set to
/// [Overflow.clipped], children cannot paint outside of the stack's box.
Overflow get overflow => _overflow;
Overflow _overflow;
set overflow (Overflow value) {
assert(value != null);
if (_overflow != value) {
_overflow = value;
markNeedsPaint();
}
}
/// How to align the non-positioned children in the stack. /// How to align the non-positioned children in the stack.
/// ///
/// The non-positioned children are placed relative to each other such that /// The non-positioned children are placed relative to each other such that
...@@ -409,7 +435,7 @@ class RenderStack extends RenderBox ...@@ -409,7 +435,7 @@ class RenderStack extends RenderBox
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
if (_hasVisualOverflow) { if (_overflow == Overflow.clip && _hasVisualOverflow) {
context.pushClipRect(needsCompositing, offset, Point.origin & size, paintStack); context.pushClipRect(needsCompositing, offset, Point.origin & size, paintStack);
} else { } else {
paintStack(context, offset); paintStack(context, offset);
......
...@@ -1394,6 +1394,7 @@ class Stack extends MultiChildRenderObjectWidget { ...@@ -1394,6 +1394,7 @@ class Stack extends MultiChildRenderObjectWidget {
Stack({ Stack({
Key key, Key key,
this.alignment: FractionalOffset.topLeft, this.alignment: FractionalOffset.topLeft,
this.overflow: Overflow.clip,
List<Widget> children: _emptyWidgetList List<Widget> children: _emptyWidgetList
}) : super(key: key, children: children); }) : super(key: key, children: children);
...@@ -1405,12 +1406,25 @@ class Stack extends MultiChildRenderObjectWidget { ...@@ -1405,12 +1406,25 @@ class Stack extends MultiChildRenderObjectWidget {
/// each non-positioned child will be located at the same global coordinate. /// each non-positioned child will be located at the same global coordinate.
final FractionalOffset alignment; final FractionalOffset alignment;
/// Whether overflowing children should be clipped. See [Overflow].
///
/// Some children in a stack might overflow its box. When this flag is set to
/// [Overflow.clipped], children cannot paint outside of the stack's box.
final Overflow overflow;
@override @override
RenderStack createRenderObject(BuildContext context) => new RenderStack(alignment: alignment); RenderStack createRenderObject(BuildContext context) {
return new RenderStack(
alignment: alignment,
overflow: overflow
);
}
@override @override
void updateRenderObject(BuildContext context, RenderStack renderObject) { void updateRenderObject(BuildContext context, RenderStack renderObject) {
renderObject.alignment = alignment; renderObject
..alignment = alignment
..overflow = overflow;
} }
} }
......
...@@ -8,6 +8,15 @@ import 'package:flutter/widgets.dart'; ...@@ -8,6 +8,15 @@ import 'package:flutter/widgets.dart';
import '../rendering/rendering_tester.dart'; import '../rendering/rendering_tester.dart';
class TestPaintingContext implements PaintingContext {
final List<Invocation> invocations = <Invocation>[];
@override
void noSuchMethod(Invocation invocation) {
invocations.add(invocation);
}
}
void main() { void main() {
testWidgets('Can construct an empty Stack', (WidgetTester tester) async { testWidgets('Can construct an empty Stack', (WidgetTester tester) async {
await tester.pumpWidget(new Stack()); await tester.pumpWidget(new Stack());
...@@ -253,4 +262,58 @@ void main() { ...@@ -253,4 +262,58 @@ void main() {
expect(renderBox.size.height, equals(12.0)); expect(renderBox.size.height, equals(12.0));
}); });
testWidgets('Stack clip test', (WidgetTester tester) async {
await tester.pumpWidget(
new Center(
child: new Stack(
children: <Widget>[
new Container(
width: 100.0,
height: 100.0
),
new Positioned(
top: 0.0,
left: 0.0,
child: new Container(
width: 200.0,
height: 200.0
)
)
]
)
)
);
RenderBox box = tester.renderObject(find.byType(Stack));
TestPaintingContext context = new TestPaintingContext();
box.paint(context, Offset.zero);
expect(context.invocations.first.memberName, equals(#pushClipRect));
await tester.pumpWidget(
new Center(
child: new Stack(
overflow: Overflow.visible,
children: <Widget>[
new Container(
width: 100.0,
height: 100.0
),
new Positioned(
top: 0.0,
left: 0.0,
child: new Container(
width: 200.0,
height: 200.0
)
)
]
)
)
);
box = tester.renderObject(find.byType(Stack));
context = new TestPaintingContext();
box.paint(context, Offset.zero);
expect(context.invocations.first.memberName, equals(#paintChild));
});
} }
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