Add clipBehavior to widgets with clipRect (#55977)
* Add clipBehavior to RenderFlex * Add clipBehavior to FittedBox * Add clipBehavior to Flex and FittedBox * Add clipBehavior to UnconstrainedBox * Add clipBehavior to Stack and Wrap * Add clipBehavior to TextEditable * Add clipBehavior to ListWheelScrollView * Add clipBehavior to SingleChildScrollView * Add clipBehavior to RenderViewportBase's widgets Those widgets are NestedScrollView and ShrinkWrappingViewport. * Fix tests * Remove enum Overflow and fix typo * Remove clipToSize * Analyze fix * Remove Mixin and other small fixes * Fix tests and respect Stack's default clipBehavior * Add Overflow back to make it non-breaking * Restore clipBehavior to make it non-breaking * Small fixes * Fix rebase
This commit is contained in:
parent
5ee5490a63
commit
cd593dae19
@ -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),
|
||||
|
@ -227,6 +227,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.'),
|
||||
@ -257,6 +258,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,
|
||||
@ -290,7 +292,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;
|
||||
@ -1241,6 +1244,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);
|
||||
@ -2133,8 +2150,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));
|
||||
|
@ -277,17 +277,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);
|
||||
}
|
||||
|
||||
@ -474,6 +477,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)
|
||||
@ -955,8 +972,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
|
||||
|
@ -143,8 +143,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),
|
||||
@ -163,11 +163,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,
|
||||
@ -178,8 +178,8 @@ class RenderListWheelViewport
|
||||
_overAndUnderCenterOpacity = overAndUnderCenterOpacity,
|
||||
_itemExtent = itemExtent,
|
||||
_squeeze = squeeze,
|
||||
_clipToSize = clipToSize,
|
||||
_renderChildrenOutsideViewport = renderChildrenOutsideViewport {
|
||||
_renderChildrenOutsideViewport = renderChildrenOutsideViewport,
|
||||
_clipBehavior = clipBehavior {
|
||||
addAll(children);
|
||||
}
|
||||
|
||||
@ -199,10 +199,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.
|
||||
@ -441,37 +441,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;
|
||||
@ -479,8 +456,8 @@ class RenderListWheelViewport
|
||||
set renderChildrenOutsideViewport(bool value) {
|
||||
assert(value != null);
|
||||
assert(
|
||||
!renderChildrenOutsideViewport || !clipToSize,
|
||||
clipToSizeAndRenderChildrenOutsideViewportConflict,
|
||||
!renderChildrenOutsideViewport || clipBehavior == Clip.none,
|
||||
clipBehaviorAndRenderChildrenOutsideViewportConflict,
|
||||
);
|
||||
if (value == _renderChildrenOutsideViewport)
|
||||
return;
|
||||
@ -489,6 +466,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();
|
||||
@ -787,12 +778,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);
|
||||
|
@ -2292,11 +2292,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;
|
||||
@ -2373,6 +2376,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;
|
||||
@ -2418,9 +2435,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);
|
||||
}
|
||||
|
@ -626,8 +626,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.
|
||||
@ -649,6 +652,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;
|
||||
@ -694,8 +711,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(() {
|
||||
|
@ -268,6 +268,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 {
|
||||
@ -326,14 +328,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);
|
||||
}
|
||||
|
||||
@ -411,17 +413,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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,8 +606,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);
|
||||
}
|
||||
@ -620,7 +622,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,17 +171,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) {
|
||||
@ -314,6 +317,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);
|
||||
@ -574,8 +591,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);
|
||||
}
|
||||
@ -1136,9 +1153,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(
|
||||
@ -1147,6 +1166,7 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
|
||||
offset: offset,
|
||||
cacheExtent: cacheExtent,
|
||||
cacheExtentStyle: cacheExtentStyle,
|
||||
clipBehavior: clipBehavior,
|
||||
) {
|
||||
addAll(children);
|
||||
if (center == null && firstChild != null)
|
||||
@ -1658,8 +1678,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);
|
||||
}
|
||||
|
||||
|
@ -101,8 +101,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
|
||||
@ -117,12 +118,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,
|
||||
@ -130,7 +133,8 @@ class RenderWrap extends RenderBox with ContainerRenderObjectMixin<RenderBox, Wr
|
||||
_runSpacing = runSpacing,
|
||||
_crossAxisAlignment = crossAxisAlignment,
|
||||
_textDirection = textDirection,
|
||||
_verticalDirection = verticalDirection {
|
||||
_verticalDirection = verticalDirection,
|
||||
_clipBehavior = clipBehavior {
|
||||
addAll(children);
|
||||
}
|
||||
|
||||
@ -326,6 +330,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);
|
||||
@ -749,8 +767,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);
|
||||
}
|
||||
|
@ -216,7 +216,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,
|
||||
|
@ -42,7 +42,6 @@ export 'package:flutter/rendering.dart' show
|
||||
MainAxisAlignment,
|
||||
MainAxisSize,
|
||||
MultiChildLayoutDelegate,
|
||||
Overflow,
|
||||
PaintingContext,
|
||||
PointerCancelEvent,
|
||||
PointerCancelEventListener,
|
||||
@ -1411,9 +1410,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.
|
||||
@ -1435,12 +1436,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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1449,7 +1457,8 @@ class FittedBox extends SingleChildRenderObjectWidget {
|
||||
renderObject
|
||||
..fit = fit
|
||||
..alignment = alignment
|
||||
..textDirection = Directionality.of(context);
|
||||
..textDirection = Directionality.of(context)
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -2231,7 +2240,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
|
||||
@ -2257,12 +2268,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
|
||||
@ -2270,6 +2288,7 @@ class UnconstrainedBox extends SingleChildRenderObjectWidget {
|
||||
textDirection: textDirection ?? Directionality.of(context),
|
||||
alignment: alignment,
|
||||
constrainedAxis: constrainedAxis,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
|
||||
@override
|
||||
@ -3214,8 +3233,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.
|
||||
@ -3252,19 +3273,29 @@ 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;
|
||||
|
||||
@override
|
||||
RenderStack createRenderObject(BuildContext context) {
|
||||
return RenderStack(
|
||||
alignment: alignment,
|
||||
textDirection: textDirection ?? Directionality.of(context),
|
||||
fit: fit,
|
||||
overflow: overflow,
|
||||
clipBehavior: overflow == Overflow.clip ? Clip.hardEdge : clipBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@ -3274,7 +3305,7 @@ class Stack extends MultiChildRenderObjectWidget {
|
||||
..alignment = alignment
|
||||
..textDirection = textDirection ?? Directionality.of(context)
|
||||
..fit = fit
|
||||
..overflow = overflow;
|
||||
..clipBehavior = overflow == Overflow.clip ? Clip.hardEdge : clipBehavior;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -3283,7 +3314,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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3809,6 +3840,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),
|
||||
@ -3816,6 +3848,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.
|
||||
@ -3900,6 +3933,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) {
|
||||
@ -3943,6 +3982,7 @@ class Flex extends MultiChildRenderObjectWidget {
|
||||
textDirection: getEffectiveTextDirection(context),
|
||||
verticalDirection: verticalDirection,
|
||||
textBaseline: textBaseline,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@ -3955,7 +3995,8 @@ class Flex extends MultiChildRenderObjectWidget {
|
||||
..crossAxisAlignment = crossAxisAlignment
|
||||
..textDirection = getEffectiveTextDirection(context)
|
||||
..verticalDirection = verticalDirection
|
||||
..textBaseline = textBaseline;
|
||||
..textBaseline = textBaseline
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -4626,8 +4667,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.
|
||||
///
|
||||
@ -4761,6 +4803,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(
|
||||
@ -4772,6 +4820,7 @@ class Wrap extends MultiChildRenderObjectWidget {
|
||||
crossAxisAlignment: crossAxisAlignment,
|
||||
textDirection: textDirection ?? Directionality.of(context),
|
||||
verticalDirection: verticalDirection,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@ -4785,7 +4834,8 @@ class Wrap extends MultiChildRenderObjectWidget {
|
||||
..runSpacing = runSpacing
|
||||
..crossAxisAlignment = crossAxisAlignment
|
||||
..textDirection = textDirection ?? Directionality.of(context)
|
||||
..verticalDirection = verticalDirection;
|
||||
..verticalDirection = verticalDirection
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -413,6 +413,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),
|
||||
@ -450,6 +451,7 @@ class EditableText extends StatefulWidget {
|
||||
assert(scrollPadding != null),
|
||||
assert(dragStartBehavior != null),
|
||||
assert(toolbarOptions != null),
|
||||
assert(clipBehavior != null),
|
||||
_strutStyle = strutStyle,
|
||||
keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline),
|
||||
inputFormatters = maxLines == 1
|
||||
@ -1120,6 +1122,11 @@ class EditableText extends StatefulWidget {
|
||||
/// {@endtemplate}
|
||||
final Iterable<String> autofillHints;
|
||||
|
||||
/// {@macro flutter.widgets.Clip}
|
||||
///
|
||||
/// Defaults to [Clip.hardEdge].
|
||||
final Clip clipBehavior;
|
||||
|
||||
@override
|
||||
EditableTextState createState() => EditableTextState();
|
||||
|
||||
@ -2089,6 +2096,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
devicePixelRatio: _devicePixelRatio,
|
||||
promptRectRange: _currentPromptRectRange,
|
||||
promptRectColor: widget.autocorrectionTextRectColor,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -2163,6 +2171,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
this.devicePixelRatio,
|
||||
this.promptRectRange,
|
||||
this.promptRectColor,
|
||||
this.clipBehavior,
|
||||
}) : assert(textDirection != null),
|
||||
assert(rendererIgnoresPointer != null),
|
||||
super(key: key);
|
||||
@ -2208,6 +2217,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
final double devicePixelRatio;
|
||||
final TextRange promptRectRange;
|
||||
final Color promptRectColor;
|
||||
final Clip clipBehavior;
|
||||
|
||||
@override
|
||||
RenderEditable createRenderObject(BuildContext context) {
|
||||
@ -2249,6 +2259,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
devicePixelRatio: devicePixelRatio,
|
||||
promptRectRange: promptRectRange,
|
||||
promptRectColor: promptRectColor,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@ -2289,6 +2300,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
..devicePixelRatio = devicePixelRatio
|
||||
..paintCursorAboveText = paintCursorAboveText
|
||||
..promptRectColor = promptRectColor
|
||||
..clipBehavior = clipBehavior
|
||||
..setPromptRectRange(promptRectRange);
|
||||
}
|
||||
}
|
||||
|
@ -579,8 +579,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),
|
||||
@ -595,11 +595,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);
|
||||
@ -619,8 +619,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),
|
||||
@ -635,11 +635,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);
|
||||
|
||||
@ -698,15 +698,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();
|
||||
}
|
||||
@ -769,10 +771,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,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -950,7 +952,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.
|
||||
@ -966,10 +968,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),
|
||||
@ -983,11 +985,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);
|
||||
|
||||
@ -1017,9 +1019,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;
|
||||
|
||||
@ -1030,6 +1029,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);
|
||||
|
||||
@ -1047,8 +1051,8 @@ class ListWheelViewport extends RenderObjectWidget {
|
||||
overAndUnderCenterOpacity: overAndUnderCenterOpacity,
|
||||
itemExtent: itemExtent,
|
||||
squeeze: squeeze,
|
||||
clipToSize: clipToSize,
|
||||
renderChildrenOutsideViewport: renderChildrenOutsideViewport,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1064,7 +1068,7 @@ class ListWheelViewport extends RenderObjectWidget {
|
||||
..overAndUnderCenterOpacity = overAndUnderCenterOpacity
|
||||
..itemExtent = itemExtent
|
||||
..squeeze = squeeze
|
||||
..clipToSize = clipToSize
|
||||
..renderChildrenOutsideViewport = renderChildrenOutsideViewport;
|
||||
..renderChildrenOutsideViewport = renderChildrenOutsideViewport
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
}
|
||||
|
@ -193,10 +193,12 @@ class NestedScrollView extends StatefulWidget {
|
||||
@required this.headerSliverBuilder,
|
||||
@required this.body,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
}) : assert(scrollDirection != null),
|
||||
assert(reverse != null),
|
||||
assert(headerSliverBuilder != null),
|
||||
assert(body != null),
|
||||
assert(clipBehavior != null),
|
||||
super(key: key);
|
||||
|
||||
/// An object that can be used to control the position to which the outer
|
||||
@ -260,6 +262,11 @@ class NestedScrollView extends StatefulWidget {
|
||||
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
|
||||
final DragStartBehavior dragStartBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.Clip}
|
||||
///
|
||||
/// Defaults to [Clip.hardEdge].
|
||||
final Clip clipBehavior;
|
||||
|
||||
/// Returns the [SliverOverlapAbsorberHandle] of the nearest ancestor
|
||||
/// [NestedScrollView].
|
||||
///
|
||||
@ -437,6 +444,7 @@ class NestedScrollViewState extends State<NestedScrollView> {
|
||||
_lastHasScrolledBody,
|
||||
),
|
||||
handle: _absorberHandle,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -452,6 +460,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,
|
||||
@ -463,6 +472,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
|
||||
);
|
||||
|
||||
final SliverOverlapAbsorberHandle handle;
|
||||
final Clip clipBehavior;
|
||||
|
||||
@override
|
||||
Widget buildViewport(
|
||||
@ -477,6 +487,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
|
||||
offset: offset,
|
||||
slivers: slivers,
|
||||
handle: handle,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1824,6 +1835,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,
|
||||
@ -1833,6 +1845,7 @@ class NestedScrollViewViewport extends Viewport {
|
||||
offset: offset,
|
||||
center: center,
|
||||
slivers: slivers,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
|
||||
/// The handle to the [SliverOverlapAbsorber] that is feeding this injector.
|
||||
@ -1849,6 +1862,7 @@ class NestedScrollViewViewport extends Viewport {
|
||||
anchor: anchor,
|
||||
offset: offset,
|
||||
handle: handle,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1862,7 +1876,8 @@ class NestedScrollViewViewport extends Viewport {
|
||||
)
|
||||
..anchor = anchor
|
||||
..offset = offset
|
||||
..handle = handle;
|
||||
..handle = handle
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1889,6 +1904,7 @@ class RenderNestedScrollViewViewport extends RenderViewport {
|
||||
List<RenderSliver> children,
|
||||
RenderSliver center,
|
||||
@required SliverOverlapAbsorberHandle handle,
|
||||
Clip clipBehavior = Clip.hardEdge,
|
||||
}) : assert(handle != null),
|
||||
_handle = handle,
|
||||
super(
|
||||
@ -1898,6 +1914,7 @@ class RenderNestedScrollViewViewport extends RenderViewport {
|
||||
anchor: anchor,
|
||||
children: children,
|
||||
center: center,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
|
||||
/// The object to notify when [markNeedsLayout] is called.
|
||||
|
@ -219,8 +219,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.'
|
||||
@ -290,6 +292,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);
|
||||
}
|
||||
@ -313,6 +320,7 @@ class SingleChildScrollView extends StatelessWidget {
|
||||
axisDirection: axisDirection,
|
||||
offset: offset,
|
||||
child: contents,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -328,17 +336,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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -347,7 +359,8 @@ class _SingleChildViewport extends SingleChildRenderObjectWidget {
|
||||
// Order dependency: The offset setter reads the axis direction.
|
||||
renderObject
|
||||
..axisDirection = axisDirection
|
||||
..offset = offset;
|
||||
..offset = offset
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,12 +370,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;
|
||||
}
|
||||
|
||||
@ -403,6 +419,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();
|
||||
@ -548,8 +578,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);
|
||||
}
|
||||
|
@ -58,12 +58,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.
|
||||
@ -118,6 +120,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.
|
||||
///
|
||||
@ -147,6 +154,7 @@ class Viewport extends MultiChildRenderObjectWidget {
|
||||
offset: offset,
|
||||
cacheExtent: cacheExtent,
|
||||
cacheExtentStyle: cacheExtentStyle,
|
||||
clipBehavior: clipBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@ -158,7 +166,8 @@ class Viewport extends MultiChildRenderObjectWidget {
|
||||
..anchor = anchor
|
||||
..offset = offset
|
||||
..cacheExtent = cacheExtent
|
||||
..cacheExtentStyle = cacheExtentStyle;
|
||||
..cacheExtentStyle = cacheExtentStyle
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -263,6 +272,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);
|
||||
@ -295,12 +305,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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -309,7 +325,8 @@ class ShrinkWrappingViewport extends MultiChildRenderObjectWidget {
|
||||
renderObject
|
||||
..axisDirection = axisDirection
|
||||
..crossAxisDirection = crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection)
|
||||
..offset = offset;
|
||||
..offset = offset
|
||||
..clipBehavior = clipBehavior;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2143,8 +2143,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,
|
||||
|
@ -670,6 +670,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());
|
||||
|
@ -24,6 +24,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(
|
||||
|
@ -52,6 +52,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()
|
||||
|
@ -431,6 +431,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,
|
||||
@ -468,6 +469,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,
|
||||
|
@ -294,3 +294,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));
|
||||
|
@ -61,6 +61,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(
|
||||
|
@ -5,6 +5,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();
|
||||
@ -151,4 +153,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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -280,6 +280,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;
|
||||
|
@ -4761,6 +4761,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 {
|
||||
|
@ -369,6 +369,7 @@ void main() {
|
||||
height: 10.0,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: SizedBox(
|
||||
width: 10.0,
|
||||
height: 50.0,
|
||||
@ -391,6 +392,7 @@ void main() {
|
||||
height: 100.0,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: SizedBox(
|
||||
width: 50.0,
|
||||
height: 10.0,
|
||||
@ -418,6 +420,7 @@ void main() {
|
||||
height: b,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.none,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: SizedBox(
|
||||
width: c,
|
||||
height: d,
|
||||
@ -472,6 +475,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() {
|
||||
|
@ -140,4 +140,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));
|
||||
});
|
||||
}
|
||||
|
@ -719,7 +719,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].'
|
||||
),
|
||||
);
|
||||
});
|
||||
@ -889,7 +889,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.'
|
||||
),
|
||||
);
|
||||
|
@ -11,6 +11,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 {
|
||||
|
@ -8,6 +8,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);
|
||||
|
||||
@ -118,6 +120,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);
|
||||
|
@ -0,0 +1,50 @@
|
||||
// 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));
|
||||
});
|
||||
}
|
@ -8,6 +8,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 {
|
||||
@ -37,6 +38,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(
|
||||
|
@ -376,6 +376,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;
|
||||
|
||||
@ -412,6 +421,7 @@ void main() {
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: Stack(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
children: const <Widget>[
|
||||
SizedBox(
|
||||
width: 100.0,
|
||||
@ -442,6 +452,7 @@ void main() {
|
||||
child: Center(
|
||||
child: Stack(
|
||||
overflow: Overflow.visible,
|
||||
clipBehavior: Clip.none,
|
||||
children: const <Widget>[
|
||||
SizedBox(
|
||||
width: 100.0,
|
||||
|
@ -733,6 +733,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),
|
||||
@ -895,4 +896,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));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user