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> {
}
}
/// 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
///
/// In a stack layout, the children are positioned on top of each other in the
......@@ -244,8 +253,11 @@ class RenderStack extends RenderBox
/// top left corners.
RenderStack({
List<RenderBox> children,
FractionalOffset alignment: FractionalOffset.topLeft
}) : _alignment = alignment {
FractionalOffset alignment: FractionalOffset.topLeft,
Overflow overflow: Overflow.clip
}) : _alignment = alignment,
_overflow = overflow {
assert(overflow != null);
addAll(children);
}
......@@ -257,6 +269,20 @@ class RenderStack extends RenderBox
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.
///
/// The non-positioned children are placed relative to each other such that
......@@ -409,7 +435,7 @@ class RenderStack extends RenderBox
@override
void paint(PaintingContext context, Offset offset) {
if (_hasVisualOverflow) {
if (_overflow == Overflow.clip && _hasVisualOverflow) {
context.pushClipRect(needsCompositing, offset, Point.origin & size, paintStack);
} else {
paintStack(context, offset);
......
......@@ -1394,6 +1394,7 @@ class Stack extends MultiChildRenderObjectWidget {
Stack({
Key key,
this.alignment: FractionalOffset.topLeft,
this.overflow: Overflow.clip,
List<Widget> children: _emptyWidgetList
}) : super(key: key, children: children);
......@@ -1405,12 +1406,25 @@ class Stack extends MultiChildRenderObjectWidget {
/// each non-positioned child will be located at the same global coordinate.
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
RenderStack createRenderObject(BuildContext context) => new RenderStack(alignment: alignment);
RenderStack createRenderObject(BuildContext context) {
return new RenderStack(
alignment: alignment,
overflow: overflow
);
}
@override
void updateRenderObject(BuildContext context, RenderStack renderObject) {
renderObject.alignment = alignment;
renderObject
..alignment = alignment
..overflow = overflow;
}
}
......
......@@ -8,6 +8,15 @@ import 'package:flutter/widgets.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() {
testWidgets('Can construct an empty Stack', (WidgetTester tester) async {
await tester.pumpWidget(new Stack());
......@@ -253,4 +262,58 @@ void main() {
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