Unverified Commit 42e02d60 authored by liyuqian's avatar liyuqian Committed by GitHub

Reland non-breaking "Add clipBehavior to widgets with clipRect #55977" (#59364)

* Revert "Revert "Add clipBehavior to widgets with clipRect (#55977)" (#58344)"

This reverts commit 1d395c5e.

* Add missed Overflow
parent 802c4b0f
File mode changed from 100755 to 100644
......@@ -224,7 +224,7 @@ class _PestoLogoState extends State<PestoLogo> {
child: SizedBox(
width: kLogoWidth,
child: Stack(
overflow: Overflow.visible,
clipBehavior: Clip.none,
children: <Widget>[
Positioned.fromRect(
rect: _imageRectTween.lerp(widget.t),
......
......@@ -230,6 +230,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
EdgeInsets floatingCursorAddedMargin = const EdgeInsets.fromLTRB(4, 4, 4, 5),
TextRange promptRectRange,
Color promptRectColor,
Clip clipBehavior = Clip.hardEdge,
@required this.textSelectionDelegate,
}) : assert(textAlign != null),
assert(textDirection != null, 'RenderEditable created without a textDirection.'),
......@@ -260,6 +261,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
assert(devicePixelRatio != null),
assert(selectionHeightStyle != null),
assert(selectionWidthStyle != null),
assert(clipBehavior != null),
_textPainter = TextPainter(
text: text,
textAlign: textAlign,
......@@ -294,7 +296,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
_obscureText = obscureText,
_readOnly = readOnly,
_forceLine = forceLine,
_promptRectRange = promptRectRange {
_promptRectRange = promptRectRange,
_clipBehavior = clipBehavior {
assert(_showCursor != null);
assert(!_showCursor.value || cursorColor != null);
this.hasFocus = hasFocus ?? false;
......@@ -1254,6 +1257,20 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
double get _caretMargin => _kCaretGap + cursorWidth;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.hardEdge;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
......@@ -2148,8 +2165,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
@override
void paint(PaintingContext context, Offset offset) {
_layoutText(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
if (_hasVisualOverflow)
context.pushClipRect(needsCompositing, offset, Offset.zero & size, _paintContents);
if (_hasVisualOverflow && clipBehavior != Clip.none)
context.pushClipRect(needsCompositing, offset, Offset.zero & size, _paintContents, clipBehavior: clipBehavior);
else
_paintContents(context, offset);
_paintHandleLayers(context, getEndpointsForSelection(selection));
......
......@@ -279,17 +279,20 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
Clip clipBehavior = Clip.none,
}) : assert(direction != null),
assert(mainAxisAlignment != null),
assert(mainAxisSize != null),
assert(crossAxisAlignment != null),
assert(clipBehavior != null),
_direction = direction,
_mainAxisAlignment = mainAxisAlignment,
_mainAxisSize = mainAxisSize,
_crossAxisAlignment = crossAxisAlignment,
_textDirection = textDirection,
_verticalDirection = verticalDirection,
_textBaseline = textBaseline {
_textBaseline = textBaseline,
_clipBehavior = clipBehavior {
addAll(children);
}
......@@ -476,6 +479,20 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
// are treated as not overflowing.
bool get _hasOverflow => _overflow > precisionErrorTolerance;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.none], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.none;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
@override
void setupParentData(RenderBox child) {
if (child.parentData is! FlexParentData)
......@@ -957,8 +974,12 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
if (size.isEmpty)
return;
// We have overflow. Clip it.
context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint);
if (clipBehavior == Clip.none) {
defaultPaint(context, offset);
} else {
// We have overflow and the clipBehavior isn't none. Clip it.
context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint, clipBehavior: clipBehavior);
}
assert(() {
// Only set this if it's null to save work. It gets reset to null if the
......
......@@ -145,8 +145,8 @@ class RenderListWheelViewport
double overAndUnderCenterOpacity = 1,
@required double itemExtent,
double squeeze = 1,
bool clipToSize = true,
bool renderChildrenOutsideViewport = false,
Clip clipBehavior = Clip.none,
List<RenderBox> children,
}) : assert(childManager != null),
assert(offset != null),
......@@ -165,11 +165,11 @@ class RenderListWheelViewport
assert(squeeze != null),
assert(squeeze > 0),
assert(itemExtent > 0),
assert(clipToSize != null),
assert(renderChildrenOutsideViewport != null),
assert(clipBehavior != null),
assert(
!renderChildrenOutsideViewport || !clipToSize,
clipToSizeAndRenderChildrenOutsideViewportConflict,
!renderChildrenOutsideViewport || clipBehavior == Clip.none,
clipBehaviorAndRenderChildrenOutsideViewportConflict,
),
_offset = offset,
_diameterRatio = diameterRatio,
......@@ -180,8 +180,8 @@ class RenderListWheelViewport
_overAndUnderCenterOpacity = overAndUnderCenterOpacity,
_itemExtent = itemExtent,
_squeeze = squeeze,
_clipToSize = clipToSize,
_renderChildrenOutsideViewport = renderChildrenOutsideViewport {
_renderChildrenOutsideViewport = renderChildrenOutsideViewport,
_clipBehavior = clipBehavior {
addAll(children);
}
......@@ -201,10 +201,10 @@ class RenderListWheelViewport
'be clipped in the z-axis and therefore not renderable. Value must be '
'between 0 and 0.01.';
/// An error message to show when [clipToSize] and [renderChildrenOutsideViewport]
/// An error message to show when [clipBehavior] and [renderChildrenOutsideViewport]
/// are set to conflicting values.
static const String clipToSizeAndRenderChildrenOutsideViewportConflict =
'Cannot renderChildrenOutsideViewport and clipToSize since children '
static const String clipBehaviorAndRenderChildrenOutsideViewportConflict =
'Cannot renderChildrenOutsideViewport and clip since children '
'rendered outside will be clipped anyway.';
/// The delegate that manages the children of this object.
......@@ -443,37 +443,14 @@ class RenderListWheelViewport
markNeedsSemanticsUpdate();
}
/// {@template flutter.rendering.wheelList.clipToSize}
/// Whether to clip painted children to the inside of this viewport.
///
/// Defaults to [true]. Must not be null.
///
/// If this is false and [renderChildrenOutsideViewport] is false, the
/// first and last children may be painted partly outside of this scroll view.
/// {@endtemplate}
bool get clipToSize => _clipToSize;
bool _clipToSize;
set clipToSize(bool value) {
assert(value != null);
assert(
!renderChildrenOutsideViewport || !clipToSize,
clipToSizeAndRenderChildrenOutsideViewportConflict,
);
if (value == _clipToSize)
return;
_clipToSize = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
/// {@template flutter.rendering.wheelList.renderChildrenOutsideViewport}
/// Whether to paint children inside the viewport only.
///
/// If false, every child will be painted. However the [Scrollable] is still
/// the size of the viewport and detects gestures inside only.
///
/// Defaults to [false]. Must not be null. Cannot be true if [clipToSize]
/// is also true since children outside the viewport will be clipped, and
/// Defaults to [false]. Must not be null. Cannot be true if [clipBehavior]
/// is not [Clip.none] since children outside the viewport will be clipped, and
/// therefore cannot render children outside the viewport.
/// {@endtemplate}
bool get renderChildrenOutsideViewport => _renderChildrenOutsideViewport;
......@@ -481,8 +458,8 @@ class RenderListWheelViewport
set renderChildrenOutsideViewport(bool value) {
assert(value != null);
assert(
!renderChildrenOutsideViewport || !clipToSize,
clipToSizeAndRenderChildrenOutsideViewportConflict,
!renderChildrenOutsideViewport || clipBehavior == Clip.none,
clipBehaviorAndRenderChildrenOutsideViewportConflict,
);
if (value == _renderChildrenOutsideViewport)
return;
......@@ -491,6 +468,20 @@ class RenderListWheelViewport
markNeedsSemanticsUpdate();
}
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.hardEdge;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
void _hasScrolled() {
markNeedsLayout();
markNeedsSemanticsUpdate();
......@@ -789,12 +780,13 @@ class RenderListWheelViewport
@override
void paint(PaintingContext context, Offset offset) {
if (childCount > 0) {
if (_clipToSize && _shouldClipAtCurrentOffset()) {
if (_shouldClipAtCurrentOffset() && clipBehavior != Clip.none) {
context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
_paintVisibleChildren,
clipBehavior: clipBehavior,
);
} else {
_paintVisibleChildren(context, offset);
......
......@@ -2294,11 +2294,14 @@ class RenderFittedBox extends RenderProxyBox {
AlignmentGeometry alignment = Alignment.center,
TextDirection textDirection,
RenderBox child,
Clip clipBehavior = Clip.none,
}) : assert(fit != null),
assert(alignment != null),
assert(clipBehavior != null),
_fit = fit,
_alignment = alignment,
_textDirection = textDirection,
_clipBehavior = clipBehavior,
super(child);
Alignment _resolvedAlignment;
......@@ -2375,6 +2378,20 @@ class RenderFittedBox extends RenderProxyBox {
bool _hasVisualOverflow;
Matrix4 _transform;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.none], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.none;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
void _clearPaintData() {
_hasVisualOverflow = null;
_transform = null;
......@@ -2420,9 +2437,9 @@ class RenderFittedBox extends RenderProxyBox {
return;
_updatePaintData();
if (child != null) {
if (_hasVisualOverflow)
if (_hasVisualOverflow && clipBehavior != Clip.none)
layer = context.pushClipRect(needsCompositing, offset, Offset.zero & size, _paintChildWithTransform,
oldLayer: layer is ClipRectLayer ? layer as ClipRectLayer : null);
oldLayer: layer is ClipRectLayer ? layer as ClipRectLayer : null, clipBehavior: clipBehavior);
else
layer = _paintChildWithTransform(context, offset);
}
......
......@@ -628,8 +628,11 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow
@required TextDirection textDirection,
Axis constrainedAxis,
RenderBox child,
Clip clipBehavior = Clip.none,
}) : assert(alignment != null),
assert(clipBehavior != null),
_constrainedAxis = constrainedAxis,
_clipBehavior = clipBehavior,
super.mixin(alignment, textDirection, child);
/// The axis to retain constraints on, if any.
......@@ -651,6 +654,20 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow
Rect _overflowChildRect = Rect.zero;
bool _isOverflowing = false;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.none], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.none;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
@override
void performLayout() {
final BoxConstraints constraints = this.constraints;
......@@ -696,8 +713,12 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow
return;
}
// We have overflow. Clip it.
context.pushClipRect(needsCompositing, offset, Offset.zero & size, super.paint);
if (clipBehavior == Clip.none) {
super.paint(context, offset);
} else {
// We have overflow and the clipBehavior isn't none. Clip it.
context.pushClipRect(needsCompositing, offset, Offset.zero & size, super.paint, clipBehavior: clipBehavior);
}
// Display the overflow indicator.
assert(() {
......
......@@ -270,6 +270,8 @@ enum StackFit {
passthrough,
}
// TODO(liyuqian): Deprecate and remove `Overflow` once its usages are removed from Google.
/// Whether overflowing children should be clipped, or their overflow be
/// visible.
enum Overflow {
......@@ -328,14 +330,14 @@ class RenderStack extends RenderBox
AlignmentGeometry alignment = AlignmentDirectional.topStart,
TextDirection textDirection,
StackFit fit = StackFit.loose,
Overflow overflow = Overflow.clip,
Clip clipBehavior = Clip.hardEdge,
}) : assert(alignment != null),
assert(fit != null),
assert(overflow != null),
assert(clipBehavior != null),
_alignment = alignment,
_textDirection = textDirection,
_fit = fit,
_overflow = overflow {
_clipBehavior = clipBehavior {
addAll(children);
}
......@@ -413,17 +415,17 @@ class RenderStack extends RenderBox
}
}
/// Whether overflowing children should be clipped. See [Overflow].
/// {@macro flutter.widgets.Clip}
///
/// Some children in a stack might overflow its box. When this flag is set to
/// [Overflow.clip], children cannot paint outside of the stack's box.
Overflow get overflow => _overflow;
Overflow _overflow;
set overflow(Overflow value) {
/// Defaults to [Clip.hardEdge], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.hardEdge;
set clipBehavior(Clip value) {
assert(value != null);
if (_overflow != value) {
_overflow = value;
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
......@@ -606,8 +608,8 @@ class RenderStack extends RenderBox
@override
void paint(PaintingContext context, Offset offset) {
if (_overflow == Overflow.clip && _hasVisualOverflow) {
context.pushClipRect(needsCompositing, offset, Offset.zero & size, paintStack);
if (clipBehavior != Clip.none && _hasVisualOverflow) {
context.pushClipRect(needsCompositing, offset, Offset.zero & size, paintStack, clipBehavior: clipBehavior);
} else {
paintStack(context, offset);
}
......@@ -622,7 +624,7 @@ class RenderStack extends RenderBox
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection));
properties.add(EnumProperty<StackFit>('fit', fit));
properties.add(EnumProperty<Overflow>('overflow', overflow));
properties.add(EnumProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.hardEdge));
}
}
......
......@@ -173,17 +173,20 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
@required ViewportOffset offset,
double cacheExtent,
CacheExtentStyle cacheExtentStyle = CacheExtentStyle.pixel,
Clip clipBehavior = Clip.hardEdge,
}) : assert(axisDirection != null),
assert(crossAxisDirection != null),
assert(offset != null),
assert(axisDirectionToAxis(axisDirection) != axisDirectionToAxis(crossAxisDirection)),
assert(cacheExtentStyle != null),
assert(cacheExtent != null || cacheExtentStyle == CacheExtentStyle.pixel),
assert(clipBehavior != null),
_axisDirection = axisDirection,
_crossAxisDirection = crossAxisDirection,
_offset = offset,
_cacheExtent = cacheExtent ?? RenderAbstractViewport.defaultCacheExtent,
_cacheExtentStyle = cacheExtentStyle;
_cacheExtentStyle = cacheExtentStyle,
_clipBehavior = clipBehavior;
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
......@@ -316,6 +319,20 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
markNeedsLayout();
}
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.hardEdge;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
......@@ -576,8 +593,8 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
void paint(PaintingContext context, Offset offset) {
if (firstChild == null)
return;
if (hasVisualOverflow) {
context.pushClipRect(needsCompositing, offset, Offset.zero & size, _paintContents);
if (hasVisualOverflow && clipBehavior != Clip.none) {
context.pushClipRect(needsCompositing, offset, Offset.zero & size, _paintContents, clipBehavior: clipBehavior);
} else {
_paintContents(context, offset);
}
......@@ -1138,9 +1155,11 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
RenderSliver center,
double cacheExtent,
CacheExtentStyle cacheExtentStyle = CacheExtentStyle.pixel,
Clip clipBehavior = Clip.hardEdge,
}) : assert(anchor != null),
assert(anchor >= 0.0 && anchor <= 1.0),
assert(cacheExtentStyle != CacheExtentStyle.viewport || cacheExtent != null),
assert(clipBehavior != null),
_anchor = anchor,
_center = center,
super(
......@@ -1149,6 +1168,7 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
offset: offset,
cacheExtent: cacheExtent,
cacheExtentStyle: cacheExtentStyle,
clipBehavior: clipBehavior,
) {
addAll(children);
if (center == null && firstChild != null)
......@@ -1660,8 +1680,14 @@ class RenderShrinkWrappingViewport extends RenderViewportBase<SliverLogicalConta
AxisDirection axisDirection = AxisDirection.down,
@required AxisDirection crossAxisDirection,
@required ViewportOffset offset,
Clip clipBehavior = Clip.hardEdge,
List<RenderSliver> children,
}) : super(axisDirection: axisDirection, crossAxisDirection: crossAxisDirection, offset: offset) {
}) : super(
axisDirection: axisDirection,
crossAxisDirection: crossAxisDirection,
offset: offset,
clipBehavior: clipBehavior,
) {
addAll(children);
}
......
......@@ -103,8 +103,9 @@ class WrapParentData extends ContainerBoxParentData<RenderBox> {
///
/// The runs themselves are then positioned in the cross axis according to the
/// [runSpacing] and [runAlignment].
class RenderWrap extends RenderBox with ContainerRenderObjectMixin<RenderBox, WrapParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, WrapParentData> {
class RenderWrap extends RenderBox
with ContainerRenderObjectMixin<RenderBox, WrapParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, WrapParentData> {
/// Creates a wrap render object.
///
/// By default, the wrap layout is horizontal and both the children and the
......@@ -119,12 +120,14 @@ class RenderWrap extends RenderBox with ContainerRenderObjectMixin<RenderBox, Wr
WrapCrossAlignment crossAxisAlignment = WrapCrossAlignment.start,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
Clip clipBehavior = Clip.none,
}) : assert(direction != null),
assert(alignment != null),
assert(spacing != null),
assert(runAlignment != null),
assert(runSpacing != null),
assert(crossAxisAlignment != null),
assert(clipBehavior != null),
_direction = direction,
_alignment = alignment,
_spacing = spacing,
......@@ -132,7 +135,8 @@ class RenderWrap extends RenderBox with ContainerRenderObjectMixin<RenderBox, Wr
_runSpacing = runSpacing,
_crossAxisAlignment = crossAxisAlignment,
_textDirection = textDirection,
_verticalDirection = verticalDirection {
_verticalDirection = verticalDirection,
_clipBehavior = clipBehavior {
addAll(children);
}
......@@ -328,6 +332,20 @@ class RenderWrap extends RenderBox with ContainerRenderObjectMixin<RenderBox, Wr
}
}
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.none], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.none;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
bool get _debugHasNecessaryDirections {
assert(direction != null);
assert(alignment != null);
......@@ -751,8 +769,8 @@ class RenderWrap extends RenderBox with ContainerRenderObjectMixin<RenderBox, Wr
void paint(PaintingContext context, Offset offset) {
// TODO(ianh): move the debug flex overflow paint logic somewhere common so
// it can be reused here
if (_hasVisualOverflow)
context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint);
if (_hasVisualOverflow && clipBehavior != Clip.none)
context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint, clipBehavior: clipBehavior);
else
defaultPaint(context, offset);
}
......
......@@ -218,7 +218,7 @@ class AnimatedCrossFade extends StatefulWidget {
/// [AnimatedCrossFadeBuilder].
static Widget defaultLayoutBuilder(Widget topChild, Key topChildKey, Widget bottomChild, Key bottomChildKey) {
return Stack(
overflow: Overflow.visible,
clipBehavior: Clip.none,
children: <Widget>[
Positioned(
key: bottomChildKey,
......
......@@ -43,8 +43,8 @@ export 'package:flutter/rendering.dart' show
LayerLink,
MainAxisAlignment,
MainAxisSize,
MultiChildLayoutDelegate,
Overflow,
MultiChildLayoutDelegate,
PaintingContext,
PointerCancelEvent,
PointerCancelEventListener,
......@@ -1413,9 +1413,11 @@ class FittedBox extends SingleChildRenderObjectWidget {
Key key,
this.fit = BoxFit.contain,
this.alignment = Alignment.center,
this.clipBehavior = Clip.hardEdge,
Widget child,
}) : assert(fit != null),
assert(alignment != null),
assert(clipBehavior != null),
super(key: key, child: child);
/// How to inscribe the child into the space allocated during layout.
......@@ -1437,12 +1439,19 @@ class FittedBox extends SingleChildRenderObjectWidget {
/// relative to text direction.
final AlignmentGeometry alignment;
// TODO(liyuqian): defaults to [Clip.none] once Google references are updated.
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
@override
RenderFittedBox createRenderObject(BuildContext context) {
return RenderFittedBox(
fit: fit,
alignment: alignment,
textDirection: Directionality.of(context),
clipBehavior: clipBehavior,
);
}
......@@ -1451,7 +1460,8 @@ class FittedBox extends SingleChildRenderObjectWidget {
renderObject
..fit = fit
..alignment = alignment
..textDirection = Directionality.of(context);
..textDirection = Directionality.of(context)
..clipBehavior = clipBehavior;
}
@override
......@@ -2233,7 +2243,9 @@ class UnconstrainedBox extends SingleChildRenderObjectWidget {
this.textDirection,
this.alignment = Alignment.center,
this.constrainedAxis,
this.clipBehavior = Clip.hardEdge,
}) : assert(alignment != null),
assert(clipBehavior != null),
super(key: key, child: child);
/// The text direction to use when interpreting the [alignment] if it is an
......@@ -2259,12 +2271,19 @@ class UnconstrainedBox extends SingleChildRenderObjectWidget {
/// will be retained.
final Axis constrainedAxis;
// TODO(liyuqian): defaults to [Clip.none] once Google references are updated.
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
@override
void updateRenderObject(BuildContext context, covariant RenderUnconstrainedBox renderObject) {
renderObject
..textDirection = textDirection ?? Directionality.of(context)
..alignment = alignment
..constrainedAxis = constrainedAxis;
..constrainedAxis = constrainedAxis
..clipBehavior = clipBehavior;
}
@override
......@@ -2272,6 +2291,7 @@ class UnconstrainedBox extends SingleChildRenderObjectWidget {
textDirection: textDirection ?? Directionality.of(context),
alignment: alignment,
constrainedAxis: constrainedAxis,
clipBehavior: clipBehavior,
);
@override
......@@ -3218,8 +3238,10 @@ class Stack extends MultiChildRenderObjectWidget {
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
}) : super(key: key, children: children);
}) : assert(clipBehavior != null),
super(key: key, children: children);
/// How to align the non-positioned and partially-positioned children in the
/// stack.
......@@ -3256,12 +3278,22 @@ class Stack extends MultiChildRenderObjectWidget {
/// ([StackFit.expand]).
final StackFit fit;
// TODO(liyuqian): Deprecate and remove [overflow] once its usages are removed from Google.
/// Whether overflowing children should be clipped. See [Overflow].
///
/// Some children in a stack might overflow its box. When this flag is set to
/// [Overflow.clip], children cannot paint outside of the stack's box.
///
/// This overrides [clipBehavior] for now due to a staged roll out without
/// breaking Google. We will remove it and only use [clipBehavior] soon.
final Overflow overflow;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
bool _debugCheckHasDirectionality(BuildContext context) {
if (alignment is AlignmentDirectional && textDirection == null) {
assert(debugCheckHasDirectionality(
......@@ -3281,7 +3313,7 @@ class Stack extends MultiChildRenderObjectWidget {
alignment: alignment,
textDirection: textDirection ?? Directionality.of(context),
fit: fit,
overflow: overflow,
clipBehavior: overflow == Overflow.visible ? Clip.none : clipBehavior,
);
}
......@@ -3292,7 +3324,7 @@ class Stack extends MultiChildRenderObjectWidget {
..alignment = alignment
..textDirection = textDirection ?? Directionality.of(context)
..fit = fit
..overflow = overflow;
..clipBehavior = overflow == Overflow.visible ? Clip.none : clipBehavior;
}
@override
......@@ -3301,7 +3333,7 @@ class Stack extends MultiChildRenderObjectWidget {
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
properties.add(EnumProperty<StackFit>('fit', fit));
properties.add(EnumProperty<Overflow>('overflow', overflow));
properties.add(EnumProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.hardEdge));
}
}
......@@ -3829,6 +3861,7 @@ class Flex extends MultiChildRenderObjectWidget {
this.textDirection,
this.verticalDirection = VerticalDirection.down,
this.textBaseline,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
}) : assert(direction != null),
assert(mainAxisAlignment != null),
......@@ -3836,6 +3869,7 @@ class Flex extends MultiChildRenderObjectWidget {
assert(crossAxisAlignment != null),
assert(verticalDirection != null),
assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null),
assert(clipBehavior != null),
super(key: key, children: children);
/// The direction to use as the main axis.
......@@ -3920,6 +3954,12 @@ class Flex extends MultiChildRenderObjectWidget {
/// If aligning items according to their baseline, which baseline to use.
final TextBaseline textBaseline;
// TODO(liyuqian): defaults to [Clip.none] once Google references are updated.
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
bool get _needTextDirection {
assert(direction != null);
switch (direction) {
......@@ -3963,6 +4003,7 @@ class Flex extends MultiChildRenderObjectWidget {
textDirection: getEffectiveTextDirection(context),
verticalDirection: verticalDirection,
textBaseline: textBaseline,
clipBehavior: clipBehavior,
);
}
......@@ -3975,7 +4016,8 @@ class Flex extends MultiChildRenderObjectWidget {
..crossAxisAlignment = crossAxisAlignment
..textDirection = getEffectiveTextDirection(context)
..verticalDirection = verticalDirection
..textBaseline = textBaseline;
..textBaseline = textBaseline
..clipBehavior = clipBehavior;
}
@override
......@@ -4646,8 +4688,9 @@ class Wrap extends MultiChildRenderObjectWidget {
this.crossAxisAlignment = WrapCrossAlignment.start,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
}) : super(key: key, children: children);
}) : assert(clipBehavior != null), super(key: key, children: children);
/// The direction to use as the main axis.
///
......@@ -4781,6 +4824,12 @@ class Wrap extends MultiChildRenderObjectWidget {
/// [verticalDirection] must not be null.
final VerticalDirection verticalDirection;
// TODO(liyuqian): defaults to [Clip.none] once Google references are updated.
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
@override
RenderWrap createRenderObject(BuildContext context) {
return RenderWrap(
......@@ -4792,6 +4841,7 @@ class Wrap extends MultiChildRenderObjectWidget {
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection ?? Directionality.of(context),
verticalDirection: verticalDirection,
clipBehavior: clipBehavior,
);
}
......@@ -4805,7 +4855,8 @@ class Wrap extends MultiChildRenderObjectWidget {
..runSpacing = runSpacing
..crossAxisAlignment = crossAxisAlignment
..textDirection = textDirection ?? Directionality.of(context)
..verticalDirection = verticalDirection;
..verticalDirection = verticalDirection
..clipBehavior = clipBehavior;
}
@override
......
......@@ -417,6 +417,7 @@ class EditableText extends StatefulWidget {
selectAll: true,
),
this.autofillHints,
this.clipBehavior = Clip.hardEdge,
}) : assert(controller != null),
assert(focusNode != null),
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
......@@ -454,6 +455,7 @@ class EditableText extends StatefulWidget {
assert(scrollPadding != null),
assert(dragStartBehavior != null),
assert(toolbarOptions != null),
assert(clipBehavior != null),
_strutStyle = strutStyle,
keyboardType = keyboardType ?? _inferKeyboardType(autofillHints: autofillHints, maxLines: maxLines),
inputFormatters = maxLines == 1
......@@ -1139,6 +1141,11 @@ class EditableText extends StatefulWidget {
/// {@macro flutter.services.autofill.autofillHints}
final Iterable<String> autofillHints;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
// Infer the keyboard type of an `EditableText` if it's not specified.
static TextInputType _inferKeyboardType({
@required Iterable<String> autofillHints,
......@@ -2268,6 +2275,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
devicePixelRatio: _devicePixelRatio,
promptRectRange: _currentPromptRectRange,
promptRectColor: widget.autocorrectionTextRectColor,
clipBehavior: widget.clipBehavior,
),
),
);
......@@ -2349,6 +2357,7 @@ class _Editable extends LeafRenderObjectWidget {
this.devicePixelRatio,
this.promptRectRange,
this.promptRectColor,
this.clipBehavior,
}) : assert(textDirection != null),
assert(rendererIgnoresPointer != null),
super(key: key);
......@@ -2395,6 +2404,7 @@ class _Editable extends LeafRenderObjectWidget {
final double devicePixelRatio;
final TextRange promptRectRange;
final Color promptRectColor;
final Clip clipBehavior;
@override
RenderEditable createRenderObject(BuildContext context) {
......@@ -2437,6 +2447,7 @@ class _Editable extends LeafRenderObjectWidget {
devicePixelRatio: devicePixelRatio,
promptRectRange: promptRectRange,
promptRectColor: promptRectColor,
clipBehavior: clipBehavior,
);
}
......@@ -2478,6 +2489,7 @@ class _Editable extends LeafRenderObjectWidget {
..devicePixelRatio = devicePixelRatio
..paintCursorAboveText = paintCursorAboveText
..promptRectColor = promptRectColor
..clipBehavior = clipBehavior
..setPromptRectRange(promptRectRange);
}
}
......
......@@ -581,8 +581,8 @@ class ListWheelScrollView extends StatefulWidget {
@required this.itemExtent,
this.squeeze = 1.0,
this.onSelectedItemChanged,
this.clipToSize = true,
this.renderChildrenOutsideViewport = false,
this.clipBehavior = Clip.hardEdge,
@required List<Widget> children,
}) : assert(children != null),
assert(diameterRatio != null),
......@@ -597,11 +597,11 @@ class ListWheelScrollView extends StatefulWidget {
assert(itemExtent > 0),
assert(squeeze != null),
assert(squeeze > 0),
assert(clipToSize != null),
assert(renderChildrenOutsideViewport != null),
assert(clipBehavior != null),
assert(
!renderChildrenOutsideViewport || !clipToSize,
RenderListWheelViewport.clipToSizeAndRenderChildrenOutsideViewportConflict,
!renderChildrenOutsideViewport || clipBehavior == Clip.none,
RenderListWheelViewport.clipBehaviorAndRenderChildrenOutsideViewportConflict,
),
childDelegate = ListWheelChildListDelegate(children: children),
super(key: key);
......@@ -621,8 +621,8 @@ class ListWheelScrollView extends StatefulWidget {
@required this.itemExtent,
this.squeeze = 1.0,
this.onSelectedItemChanged,
this.clipToSize = true,
this.renderChildrenOutsideViewport = false,
this.clipBehavior = Clip.hardEdge,
@required this.childDelegate,
}) : assert(childDelegate != null),
assert(diameterRatio != null),
......@@ -637,11 +637,11 @@ class ListWheelScrollView extends StatefulWidget {
assert(itemExtent > 0),
assert(squeeze != null),
assert(squeeze > 0),
assert(clipToSize != null),
assert(renderChildrenOutsideViewport != null),
assert(clipBehavior != null),
assert(
!renderChildrenOutsideViewport || !clipToSize,
RenderListWheelViewport.clipToSizeAndRenderChildrenOutsideViewportConflict,
!renderChildrenOutsideViewport || clipBehavior == Clip.none,
RenderListWheelViewport.clipBehaviorAndRenderChildrenOutsideViewportConflict,
),
super(key: key);
......@@ -700,15 +700,17 @@ class ListWheelScrollView extends StatefulWidget {
/// On optional listener that's called when the centered item changes.
final ValueChanged<int> onSelectedItemChanged;
/// {@macro flutter.rendering.wheelList.clipToSize}
final bool clipToSize;
/// {@macro flutter.rendering.wheelList.renderChildrenOutsideViewport}
final bool renderChildrenOutsideViewport;
/// A delegate that helps lazily instantiating child.
final ListWheelChildDelegate childDelegate;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
@override
_ListWheelScrollViewState createState() => _ListWheelScrollViewState();
}
......@@ -771,10 +773,10 @@ class _ListWheelScrollViewState extends State<ListWheelScrollView> {
overAndUnderCenterOpacity: widget.overAndUnderCenterOpacity,
itemExtent: widget.itemExtent,
squeeze: widget.squeeze,
clipToSize: widget.clipToSize,
renderChildrenOutsideViewport: widget.renderChildrenOutsideViewport,
offset: offset,
childDelegate: widget.childDelegate,
clipBehavior: widget.clipBehavior,
);
},
),
......@@ -952,7 +954,7 @@ class ListWheelViewport extends RenderObjectWidget {
///
/// The [itemExtent] argument in pixels must be provided and must be positive.
///
/// The [clipToSize] argument defaults to true and must not be null.
/// The [clipBehavior] argument defaults to [Clip.hardEdge] and must not be null.
///
/// The [renderChildrenOutsideViewport] argument defaults to false and must
/// not be null.
......@@ -968,10 +970,10 @@ class ListWheelViewport extends RenderObjectWidget {
this.overAndUnderCenterOpacity = 1.0,
@required this.itemExtent,
this.squeeze = 1.0,
this.clipToSize = true,
this.renderChildrenOutsideViewport = false,
@required this.offset,
@required this.childDelegate,
this.clipBehavior = Clip.hardEdge,
}) : assert(childDelegate != null),
assert(offset != null),
assert(diameterRatio != null),
......@@ -985,11 +987,11 @@ class ListWheelViewport extends RenderObjectWidget {
assert(itemExtent > 0),
assert(squeeze != null),
assert(squeeze > 0),
assert(clipToSize != null),
assert(renderChildrenOutsideViewport != null),
assert(clipBehavior != null),
assert(
!renderChildrenOutsideViewport || !clipToSize,
RenderListWheelViewport.clipToSizeAndRenderChildrenOutsideViewportConflict,
!renderChildrenOutsideViewport || clipBehavior == Clip.none,
RenderListWheelViewport.clipBehaviorAndRenderChildrenOutsideViewportConflict,
),
super(key: key);
......@@ -1019,9 +1021,6 @@ class ListWheelViewport extends RenderObjectWidget {
/// Defaults to 1.
final double squeeze;
/// {@macro flutter.rendering.wheelList.clipToSize}
final bool clipToSize;
/// {@macro flutter.rendering.wheelList.renderChildrenOutsideViewport}
final bool renderChildrenOutsideViewport;
......@@ -1032,6 +1031,11 @@ class ListWheelViewport extends RenderObjectWidget {
/// A delegate that lazily instantiates children.
final ListWheelChildDelegate childDelegate;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.none].
final Clip clipBehavior;
@override
ListWheelElement createElement() => ListWheelElement(this);
......@@ -1049,8 +1053,8 @@ class ListWheelViewport extends RenderObjectWidget {
overAndUnderCenterOpacity: overAndUnderCenterOpacity,
itemExtent: itemExtent,
squeeze: squeeze,
clipToSize: clipToSize,
renderChildrenOutsideViewport: renderChildrenOutsideViewport,
clipBehavior: clipBehavior,
);
}
......@@ -1066,7 +1070,7 @@ class ListWheelViewport extends RenderObjectWidget {
..overAndUnderCenterOpacity = overAndUnderCenterOpacity
..itemExtent = itemExtent
..squeeze = squeeze
..clipToSize = clipToSize
..renderChildrenOutsideViewport = renderChildrenOutsideViewport;
..renderChildrenOutsideViewport = renderChildrenOutsideViewport
..clipBehavior = clipBehavior;
}
}
......@@ -375,11 +375,13 @@ class NestedScrollView extends StatefulWidget {
@required this.body,
this.dragStartBehavior = DragStartBehavior.start,
this.floatHeaderSlivers = false,
this.clipBehavior = Clip.hardEdge,
}) : assert(scrollDirection != null),
assert(reverse != null),
assert(headerSliverBuilder != null),
assert(body != null),
assert(floatHeaderSlivers != null),
assert(clipBehavior != null),
super(key: key);
/// An object that can be used to control the position to which the outer
......@@ -450,6 +452,11 @@ class NestedScrollView extends StatefulWidget {
/// is expected to float. This cannot be null.
final bool floatHeaderSlivers;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
/// Returns the [SliverOverlapAbsorberHandle] of the nearest ancestor
/// [NestedScrollView].
///
......@@ -628,6 +635,7 @@ class NestedScrollViewState extends State<NestedScrollView> {
_lastHasScrolledBody,
),
handle: _absorberHandle,
clipBehavior: widget.clipBehavior,
);
},
),
......@@ -643,6 +651,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
@required ScrollController controller,
@required List<Widget> slivers,
@required this.handle,
@required this.clipBehavior,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super(
scrollDirection: scrollDirection,
......@@ -654,6 +663,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
);
final SliverOverlapAbsorberHandle handle;
final Clip clipBehavior;
@override
Widget buildViewport(
......@@ -668,6 +678,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
offset: offset,
slivers: slivers,
handle: handle,
clipBehavior: clipBehavior,
);
}
}
......@@ -2034,6 +2045,7 @@ class NestedScrollViewViewport extends Viewport {
Key center,
List<Widget> slivers = const <Widget>[],
@required this.handle,
Clip clipBehavior = Clip.hardEdge,
}) : assert(handle != null),
super(
key: key,
......@@ -2043,6 +2055,7 @@ class NestedScrollViewViewport extends Viewport {
offset: offset,
center: center,
slivers: slivers,
clipBehavior: clipBehavior,
);
/// The handle to the [SliverOverlapAbsorber] that is feeding this injector.
......@@ -2059,6 +2072,7 @@ class NestedScrollViewViewport extends Viewport {
anchor: anchor,
offset: offset,
handle: handle,
clipBehavior: clipBehavior,
);
}
......@@ -2072,7 +2086,8 @@ class NestedScrollViewViewport extends Viewport {
)
..anchor = anchor
..offset = offset
..handle = handle;
..handle = handle
..clipBehavior = clipBehavior;
}
@override
......@@ -2099,6 +2114,7 @@ class RenderNestedScrollViewViewport extends RenderViewport {
List<RenderSliver> children,
RenderSliver center,
@required SliverOverlapAbsorberHandle handle,
Clip clipBehavior = Clip.hardEdge,
}) : assert(handle != null),
_handle = handle,
super(
......@@ -2108,6 +2124,7 @@ class RenderNestedScrollViewViewport extends RenderViewport {
anchor: anchor,
children: children,
center: center,
clipBehavior: clipBehavior,
);
/// The object to notify when [markNeedsLayout] is called.
......
......@@ -221,8 +221,10 @@ class SingleChildScrollView extends StatelessWidget {
this.controller,
this.child,
this.dragStartBehavior = DragStartBehavior.start,
this.clipBehavior = Clip.hardEdge,
}) : assert(scrollDirection != null),
assert(dragStartBehavior != null),
assert(clipBehavior != null),
assert(!(controller != null && primary == true),
'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. '
'You cannot both set primary to true and pass an explicit controller.'
......@@ -292,6 +294,11 @@ class SingleChildScrollView extends StatelessWidget {
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
AxisDirection _getDirection(BuildContext context) {
return getAxisDirectionFromAxisReverseAndDirectionality(context, scrollDirection, reverse);
}
......@@ -315,6 +322,7 @@ class SingleChildScrollView extends StatelessWidget {
axisDirection: axisDirection,
offset: offset,
child: contents,
clipBehavior: clipBehavior,
);
},
);
......@@ -330,17 +338,21 @@ class _SingleChildViewport extends SingleChildRenderObjectWidget {
this.axisDirection = AxisDirection.down,
this.offset,
Widget child,
@required this.clipBehavior,
}) : assert(axisDirection != null),
assert(clipBehavior != null),
super(key: key, child: child);
final AxisDirection axisDirection;
final ViewportOffset offset;
final Clip clipBehavior;
@override
_RenderSingleChildViewport createRenderObject(BuildContext context) {
return _RenderSingleChildViewport(
axisDirection: axisDirection,
offset: offset,
clipBehavior: clipBehavior,
);
}
......@@ -349,7 +361,8 @@ class _SingleChildViewport extends SingleChildRenderObjectWidget {
// Order dependency: The offset setter reads the axis direction.
renderObject
..axisDirection = axisDirection
..offset = offset;
..offset = offset
..clipBehavior = clipBehavior;
}
}
......@@ -359,12 +372,15 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
@required ViewportOffset offset,
double cacheExtent = RenderAbstractViewport.defaultCacheExtent,
RenderBox child,
@required Clip clipBehavior,
}) : assert(axisDirection != null),
assert(offset != null),
assert(cacheExtent != null),
assert(clipBehavior != null),
_axisDirection = axisDirection,
_offset = offset,
_cacheExtent = cacheExtent {
_cacheExtent = cacheExtent,
_clipBehavior = clipBehavior {
this.child = child;
}
......@@ -405,6 +421,20 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
markNeedsLayout();
}
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.none], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.none;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
}
void _hasScrolled() {
markNeedsPaint();
markNeedsSemanticsUpdate();
......@@ -550,8 +580,8 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
context.paintChild(child, offset + paintOffset);
}
if (_shouldClipAtPaintOffset(paintOffset)) {
context.pushClipRect(needsCompositing, offset, Offset.zero & size, paintContents);
if (_shouldClipAtPaintOffset(paintOffset) && clipBehavior != Clip.none) {
context.pushClipRect(needsCompositing, offset, Offset.zero & size, paintContents, clipBehavior: clipBehavior);
} else {
paintContents(context, offset);
}
......
......@@ -60,12 +60,14 @@ class Viewport extends MultiChildRenderObjectWidget {
this.center,
this.cacheExtent,
this.cacheExtentStyle = CacheExtentStyle.pixel,
this.clipBehavior = Clip.hardEdge,
List<Widget> slivers = const <Widget>[],
}) : assert(offset != null),
assert(slivers != null),
assert(center == null || slivers.where((Widget child) => child.key == center).length == 1),
assert(cacheExtentStyle != null),
assert(cacheExtentStyle != CacheExtentStyle.viewport || cacheExtent != null),
assert(clipBehavior != null),
super(key: key, children: slivers);
/// The direction in which the [offset]'s [ViewportOffset.pixels] increases.
......@@ -120,6 +122,11 @@ class Viewport extends MultiChildRenderObjectWidget {
/// {@macro flutter.rendering.viewport.cacheExtentStyle}
final CacheExtentStyle cacheExtentStyle;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.none].
final Clip clipBehavior;
/// Given a [BuildContext] and an [AxisDirection], determine the correct cross
/// axis direction.
///
......@@ -149,6 +156,7 @@ class Viewport extends MultiChildRenderObjectWidget {
offset: offset,
cacheExtent: cacheExtent,
cacheExtentStyle: cacheExtentStyle,
clipBehavior: clipBehavior,
);
}
......@@ -160,7 +168,8 @@ class Viewport extends MultiChildRenderObjectWidget {
..anchor = anchor
..offset = offset
..cacheExtent = cacheExtent
..cacheExtentStyle = cacheExtentStyle;
..cacheExtentStyle = cacheExtentStyle
..clipBehavior = clipBehavior;
}
@override
......@@ -265,6 +274,7 @@ class ShrinkWrappingViewport extends MultiChildRenderObjectWidget {
this.axisDirection = AxisDirection.down,
this.crossAxisDirection,
@required this.offset,
this.clipBehavior = Clip.hardEdge,
List<Widget> slivers = const <Widget>[],
}) : assert(offset != null),
super(key: key, children: slivers);
......@@ -297,12 +307,18 @@ class ShrinkWrappingViewport extends MultiChildRenderObjectWidget {
/// Typically a [ScrollPosition].
final ViewportOffset offset;
/// {@macro flutter.widgets.Clip}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
@override
RenderShrinkWrappingViewport createRenderObject(BuildContext context) {
return RenderShrinkWrappingViewport(
axisDirection: axisDirection,
crossAxisDirection: crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection),
offset: offset,
clipBehavior: clipBehavior,
);
}
......@@ -311,7 +327,8 @@ class ShrinkWrappingViewport extends MultiChildRenderObjectWidget {
renderObject
..axisDirection = axisDirection
..crossAxisDirection = crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection)
..offset = offset;
..offset = offset
..clipBehavior = clipBehavior;
}
@override
......
......@@ -2145,8 +2145,8 @@ void main() {
// hard coded 16px margin in the dropdown code, so that
// this hint aligns "properly" with the menu.
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.topCenter,
overflow: Overflow.visible,
children: <Widget>[
PositionedDirectional(
width: constraints.maxWidth + hintPaddingOffset,
......
......@@ -672,6 +672,33 @@ void main() {
expect(unconstrained.size.height, equals(100.0), reason: 'constrained height');
});
test('clipBehavior is respected', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none
final RenderUnconstrainedBox defaultBox = RenderUnconstrainedBox(
alignment: Alignment.center,
textDirection: TextDirection.ltr,
child: box200x200,
);
layout(defaultBox, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
defaultBox.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.none));
for (final Clip clip in Clip.values) {
final RenderUnconstrainedBox box = RenderUnconstrainedBox(
alignment: Alignment.center,
textDirection: TextDirection.ltr,
child: box200x200,
clipBehavior: clip,
);
layout(box, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
box.paint(context, Offset.zero);
expect(context.clipBehavior, equals(clip));
}
});
group('hit testing', () {
test('BoxHitTestResult wrapping HitTestResult', () {
final HitTestEntry entry1 = HitTestEntry(_DummyHitTestTarget());
......
......@@ -26,6 +26,44 @@ class FakeEditableTextState with TextSelectionDelegate {
}
void main() {
test('RenderEditable respects clipBehavior', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext();
final String longString = 'a' * 10000;
// By default, clipBehavior should be Clip.none
final RenderEditable defaultEditable = RenderEditable(
text: TextSpan(text: longString),
textDirection: TextDirection.ltr,
startHandleLayerLink: LayerLink(),
endHandleLayerLink: LayerLink(),
offset: ViewportOffset.zero(),
textSelectionDelegate: FakeEditableTextState(),
selection: const TextSelection(baseOffset: 0, extentOffset: 0),
);
layout(defaultEditable, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
defaultEditable.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.hardEdge));
context.clipBehavior = Clip.none; // Reset as Clip.none won't write into clipBehavior.
for (final Clip clip in Clip.values) {
final RenderEditable editable = RenderEditable(
text: TextSpan(text: longString),
textDirection: TextDirection.ltr,
startHandleLayerLink: LayerLink(),
endHandleLayerLink: LayerLink(),
offset: ViewportOffset.zero(),
textSelectionDelegate: FakeEditableTextState(),
selection: const TextSelection(baseOffset: 0, extentOffset: 0),
clipBehavior: clip,
);
layout(editable, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
editable.paint(context, Offset.zero);
expect(context.clipBehavior, equals(clip));
}
});
test('editable intrinsics', () {
final TextSelectionDelegate delegate = FakeEditableTextState();
final RenderEditable editable = RenderEditable(
......
......@@ -54,6 +54,24 @@ void main() {
FlutterError.onError = oldHandler;
});
test('Clip behavior is respected', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none
final RenderFlex defaultFlex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200]);
layout(defaultFlex, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
defaultFlex.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.none));
for (final Clip clip in Clip.values) {
final RenderFlex flex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200], clipBehavior: clip);
layout(flex, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
flex.paint(context, Offset.zero);
expect(context.clipBehavior, equals(clip));
}
});
test('Vertical Overflow', () {
final RenderConstrainedBox flexible = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.expand()
......
......@@ -433,6 +433,7 @@ void main() {
_testLayerReuse<ClipRectLayer>(RenderFittedBox(
alignment: Alignment.center,
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
......@@ -470,6 +471,24 @@ void main() {
_testFittedBoxWithClipRectLayer();
});
test('RenderFittedBox respects clipBehavior', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none
final RenderFittedBox defaultBox = RenderFittedBox(child: box200x200, fit: BoxFit.none);
layout(defaultBox, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
defaultBox.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.none));
for (final Clip clip in Clip.values) {
final RenderFittedBox box = RenderFittedBox(child: box200x200, fit: BoxFit.none, clipBehavior: clip);
layout(box, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
box.paint(context, Offset.zero);
expect(context.clipBehavior, equals(clip));
}
});
test('RenderMouseRegion can change properties when detached', () {
renderer.initMouseTracker(MouseTracker(
renderer.pointerRouter,
......
......@@ -296,3 +296,26 @@ class FakeTicker implements Ticker {
return DiagnosticsProperty<Ticker>(name, this, style: DiagnosticsTreeStyle.errorProperty);
}
}
class TestClipPaintingContext extends PaintingContext {
TestClipPaintingContext() : super(ContainerLayer(), Rect.zero);
@override
ClipRectLayer pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, {Clip clipBehavior = Clip.hardEdge, ClipRectLayer oldLayer}) {
this.clipBehavior = clipBehavior;
return null;
}
Clip clipBehavior = Clip.none;
}
void expectOverflowedErrors() {
final FlutterErrorDetails errorDetails = renderer.takeFlutterErrorDetails();
final bool overflowed = errorDetails.toString().contains('overflowed');
if (!overflowed) {
FlutterError.reportError(errorDetails);
}
}
RenderConstrainedBox get box200x200 =>
RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 200.0, width: 200.0));
......@@ -63,6 +63,33 @@ void main() {
expect(stack.size.height, equals(100.0));
});
test('Stack respects clipBehavior', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none
final RenderStack defaultStack = RenderStack(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200]);
layout(defaultStack, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
defaultStack.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.none));
for (final Clip clip in Clip.values) {
final RenderBox child = box200x200;
final RenderStack stack = RenderStack(
textDirection: TextDirection.ltr,
children: <RenderBox>[child],
clipBehavior: clip,
);
{ // Make sure that the child is positioned so the stack will consider it as overflowed.
final StackParentData parentData = child.parentData as StackParentData;
parentData.left = parentData.right = 0;
}
layout(stack, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
stack.paint(context, Offset.zero);
expect(context.clipBehavior, equals(clip));
}
});
group('RenderIndexedStack', () {
test('visitChildrenForSemantics only visits displayed child', () {
final RenderBox child1 = RenderConstrainedBox(
......
......@@ -7,6 +7,8 @@
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'rendering_tester.dart';
void main() {
test('Wrap test; toStringDeep', () {
final RenderWrap renderWrap = RenderWrap();
......@@ -153,4 +155,22 @@ void main() {
expect(renderWrap.computeMinIntrinsicWidth(79), 80);
expect(renderWrap.computeMinIntrinsicWidth(80), 80);
});
test('Wrap respects clipBehavior', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none
final RenderWrap defaultWrap = RenderWrap(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200]);
layout(defaultWrap, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
defaultWrap.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.none));
for (final Clip clip in Clip.values) {
final RenderWrap wrap = RenderWrap(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200], clipBehavior: clip);
layout(wrap, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
wrap.paint(context, Offset.zero);
expect(context.clipBehavior, equals(clip));
}
});
}
......@@ -282,6 +282,15 @@ void main() {
);
});
testWidgets('UnconstrainedBox can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(const UnconstrainedBox());
final RenderUnconstrainedBox renderObject = tester.allRenderObjects.whereType<RenderUnconstrainedBox>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
await tester.pumpWidget(const UnconstrainedBox(clipBehavior: Clip.antiAlias));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
});
group('ColoredBox', () {
_MockCanvas mockCanvas;
_MockPaintingContext mockContext;
......
......@@ -5020,6 +5020,48 @@ void main() {
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
});
testWidgets('EditableText can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(MediaQuery(
data: const MediaQueryData(devicePixelRatio: 1.0),
child: Directionality(
textDirection: TextDirection.ltr,
child: FocusScope(
node: focusScopeNode,
autofocus: true,
child: EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
),
),
));
final RenderEditable renderObject = tester.allRenderObjects.whereType<RenderEditable>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
await tester.pumpWidget(MediaQuery(
data: const MediaQueryData(devicePixelRatio: 1.0),
child: Directionality(
textDirection: TextDirection.ltr,
child: FocusScope(
node: focusScopeNode,
autofocus: true,
child: EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
clipBehavior: Clip.antiAlias,
),
),
),
));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
});
}
class MockTextFormatter extends TextInputFormatter {
......
......@@ -371,6 +371,7 @@ void main() {
height: 10.0,
child: FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: 10.0,
height: 50.0,
......@@ -393,6 +394,7 @@ void main() {
height: 100.0,
child: FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: 50.0,
height: 10.0,
......@@ -420,6 +422,7 @@ void main() {
height: b,
child: FittedBox(
fit: BoxFit.none,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: c,
height: d,
......@@ -474,6 +477,15 @@ void main() {
await tester.tap(find.byKey(key1));
expect(_pointerDown, isTrue);
});
testWidgets('Can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(FittedBox(fit: BoxFit.none, child: Container()));
final RenderFittedBox renderObject = tester.allRenderObjects.whereType<RenderFittedBox>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
await tester.pumpWidget(FittedBox(fit: BoxFit.none, child: Container(), clipBehavior: Clip.antiAlias));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
});
}
List<Type> getLayers() {
......
......@@ -142,4 +142,13 @@ void main() {
final String message = tester.takeException().toString();
expect(message, contains('\nSee also:'));
});
testWidgets('Can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(Flex(direction: Axis.vertical));
final RenderFlex renderObject = tester.allRenderObjects.whereType<RenderFlex>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
await tester.pumpWidget(Flex(direction: Axis.vertical, clipBehavior: Clip.antiAlias));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
});
}
......@@ -721,7 +721,7 @@ void main() {
equalsIgnoringHashCodes(
'Duplicate keys found.\n'
'If multiple keyed nodes exist as children of another node, they must have unique keys.\n'
'Stack(alignment: AlignmentDirectional.topStart, textDirection: ltr, fit: loose, overflow: clip) has multiple children with key [GlobalKey#00000 problematic].'
'Stack(alignment: AlignmentDirectional.topStart, textDirection: ltr, fit: loose) has multiple children with key [GlobalKey#00000 problematic].'
),
);
});
......@@ -891,7 +891,7 @@ void main() {
'The specific parent that did not update after having one or more children forcibly '
'removed due to GlobalKey reparenting is:\n'
'- Stack(alignment: AlignmentDirectional.topStart, textDirection: ltr, fit: loose, '
'overflow: clip, renderObject: RenderStack#00000)\n'
'renderObject: RenderStack#00000)\n'
'A GlobalKey can only be specified on one widget at a time in the widget tree.'
),
);
......
......@@ -13,6 +13,44 @@ import '../rendering/mock_canvas.dart';
import '../rendering/rendering_tester.dart';
void main() {
testWidgets('ListWheelScrollView respects clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: ListWheelScrollView(
itemExtent: 2000.0, // huge extent to trigger clip
children: <Widget>[Container()],
),
),
);
// 1st, check that the render object has received the default clip behavior.
final RenderListWheelViewport renderObject = tester.allRenderObjects.whereType<RenderListWheelViewport>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
// 2nd, check that the painting context has received the default clip behavior.
final TestClipPaintingContext context = TestClipPaintingContext();
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.hardEdge));
// 3rd, pump a new widget to check that the render object can update its clip behavior.
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: ListWheelScrollView(
itemExtent: 2000.0, // huge extent to trigger clip
children: <Widget>[Container()],
clipBehavior: Clip.antiAlias,
),
),
);
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
// 4th, check that a non-default clip behavior can be sent to the painting context.
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.antiAlias));
});
group('construction check', () {
testWidgets('ListWheelScrollView needs positive diameter ratio', (WidgetTester tester) async {
try {
......
......@@ -10,6 +10,8 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/rendering.dart';
import '../rendering/rendering_tester.dart';
class _CustomPhysics extends ClampingScrollPhysics {
const _CustomPhysics({ ScrollPhysics parent }) : super(parent: parent);
......@@ -120,6 +122,55 @@ Widget buildTest({
}
void main() {
testWidgets('NestedScrollView respects clipBehavior', (WidgetTester tester) async {
Widget build(NestedScrollView nestedScrollView) {
return Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
],
child: Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: nestedScrollView,
),
),
);
}
await tester.pumpWidget(build(
NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) => <Widget>[const SliverAppBar()],
body: Container(height: 2000.0),
)
));
// 1st, check that the render object has received the default clip behavior.
final RenderNestedScrollViewViewport renderObject = tester.allRenderObjects.whereType<RenderNestedScrollViewViewport>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
// 2nd, check that the painting context has received the default clip behavior.
final TestClipPaintingContext context = TestClipPaintingContext();
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.hardEdge));
// 3rd, pump a new widget to check that the render object can update its clip behavior.
await tester.pumpWidget(build(
NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) => <Widget>[const SliverAppBar()],
body: Container(height: 2000.0),
clipBehavior: Clip.antiAlias,
)
));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
// 4th, check that a non-default clip behavior can be sent to the painting context.
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.antiAlias));
});
testWidgets('NestedScrollView overscroll and release and hold', (WidgetTester tester) async {
await tester.pumpWidget(buildTest());
expect(find.text('aaa2'), findsOneWidget);
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import '../rendering/rendering_tester.dart';
void main() {
testWidgets('ShrinkWrappingViewport respects clipBehavior', (WidgetTester tester) async {
Widget build(ShrinkWrappingViewport child) {
return Directionality(
textDirection: TextDirection.ltr,
child: child,
);
}
await tester.pumpWidget(build(
ShrinkWrappingViewport(
offset: ViewportOffset.zero(),
slivers: <Widget>[SliverToBoxAdapter(child: Container(height: 2000.0))],
)
));
// 1st, check that the render object has received the default clip behavior.
final RenderShrinkWrappingViewport renderObject = tester.allRenderObjects.whereType<RenderShrinkWrappingViewport>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
// 2nd, check that the painting context has received the default clip behavior.
final TestClipPaintingContext context = TestClipPaintingContext();
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.hardEdge));
// 3rd, pump a new widget to check that the render object can update its clip behavior.
await tester.pumpWidget(build(
ShrinkWrappingViewport(
offset: ViewportOffset.zero(),
slivers: <Widget>[SliverToBoxAdapter(child: Container(height: 2000.0))],
clipBehavior: Clip.antiAlias,
)
));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
// 4th, check that a non-default clip behavior can be sent to the painting context.
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.antiAlias));
});
}
......@@ -10,6 +10,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import '../rendering/rendering_tester.dart';
import 'semantics_tester.dart';
class TestScrollPosition extends ScrollPositionWithSingleContext {
......@@ -39,6 +40,27 @@ class TestScrollController extends ScrollController {
}
void main() {
testWidgets('SingleChildScrollView respects clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(SingleChildScrollView(child: Container(height: 2000.0)));
// 1st, check that the render object has received the default clip behavior.
final dynamic renderObject = tester.allRenderObjects.where((RenderObject o) => o.runtimeType.toString() == '_RenderSingleChildViewport').first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
// 2nd, check that the painting context has received the default clip behavior.
final TestClipPaintingContext context = TestClipPaintingContext();
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.hardEdge));
// 3rd, pump a new widget to check that the render object can update its clip behavior.
await tester.pumpWidget(SingleChildScrollView(clipBehavior: Clip.antiAlias, child: Container(height: 2000.0)));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
// 4th, check that a non-default clip behavior can be sent to the painting context.
renderObject.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.antiAlias));
});
testWidgets('SingleChildScrollView control test', (WidgetTester tester) async {
await tester.pumpWidget(SingleChildScrollView(
child: Container(
......
......@@ -378,6 +378,15 @@ void main() {
expect(renderBox.size.height, equals(12.0));
});
testWidgets('Can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(Stack(textDirection: TextDirection.ltr));
final RenderStack renderObject = tester.allRenderObjects.whereType<RenderStack>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
await tester.pumpWidget(Stack(textDirection: TextDirection.ltr, clipBehavior: Clip.hardEdge));
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
});
testWidgets('IndexedStack with null index', (WidgetTester tester) async {
bool tapped;
......@@ -414,6 +423,7 @@ void main() {
textDirection: TextDirection.ltr,
child: Center(
child: Stack(
clipBehavior: Clip.hardEdge,
children: const <Widget>[
SizedBox(
width: 100.0,
......@@ -444,6 +454,7 @@ void main() {
child: Center(
child: Stack(
overflow: Overflow.visible,
clipBehavior: Clip.none,
children: const <Widget>[
SizedBox(
width: 100.0,
......
......@@ -735,6 +735,7 @@ void main() {
await tester.pumpWidget(Wrap(
textDirection: TextDirection.ltr,
clipBehavior: Clip.hardEdge,
children: const <Widget>[
SizedBox(width: 500.0, height: 500.0),
SizedBox(width: 500.0, height: 500.0),
......@@ -897,4 +898,13 @@ void main() {
const Offset(0.0, 20.0),
]);
});
testWidgets('Wrap can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(Wrap(textDirection: TextDirection.ltr));
final RenderWrap renderObject = tester.allRenderObjects.whereType<RenderWrap>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
await tester.pumpWidget(Wrap(textDirection: TextDirection.ltr, clipBehavior: Clip.antiAlias));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
});
}
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