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
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/pesto_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/pesto_demo.dart
index 328dce7..bac6263 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/pesto_demo.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/pesto_demo.dart
@@ -224,7 +224,7 @@
         child: SizedBox(
           width: kLogoWidth,
           child: Stack(
-            overflow: Overflow.visible,
+            clipBehavior: Clip.none,
             children: <Widget>[
               Positioned.fromRect(
                 rect: _imageRectTween.lerp(widget.t),
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
index 0f31f6a..4d5f46b 100644
--- a/packages/flutter/lib/src/rendering/editable.dart
+++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -227,6 +227,7 @@
     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 @@
        assert(devicePixelRatio != null),
        assert(selectionHeightStyle != null),
        assert(selectionWidthStyle != null),
+       assert(clipBehavior != null),
        _textPainter = TextPainter(
          text: text,
          textAlign: textAlign,
@@ -290,7 +292,8 @@
        _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 @@
 
   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 @@
   @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));
diff --git a/packages/flutter/lib/src/rendering/flex.dart b/packages/flutter/lib/src/rendering/flex.dart
index 45cdc72..91819f3 100644
--- a/packages/flutter/lib/src/rendering/flex.dart
+++ b/packages/flutter/lib/src/rendering/flex.dart
@@ -277,17 +277,20 @@
     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 @@
   // 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 @@
     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
diff --git a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart
index 7ae0aab..92baf8d 100644
--- a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart
+++ b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart
@@ -143,8 +143,8 @@
     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 @@
        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 @@
        _overAndUnderCenterOpacity = overAndUnderCenterOpacity,
        _itemExtent = itemExtent,
        _squeeze = squeeze,
-       _clipToSize = clipToSize,
-       _renderChildrenOutsideViewport = renderChildrenOutsideViewport {
+       _renderChildrenOutsideViewport = renderChildrenOutsideViewport,
+       _clipBehavior = clipBehavior {
     addAll(children);
   }
 
@@ -199,10 +199,10 @@
       '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 @@
     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 @@
   set renderChildrenOutsideViewport(bool value) {
     assert(value != null);
     assert(
-      !renderChildrenOutsideViewport || !clipToSize,
-      clipToSizeAndRenderChildrenOutsideViewportConflict,
+      !renderChildrenOutsideViewport || clipBehavior == Clip.none,
+      clipBehaviorAndRenderChildrenOutsideViewportConflict,
     );
     if (value == _renderChildrenOutsideViewport)
       return;
@@ -489,6 +466,20 @@
     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 @@
   @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);
diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart
index 0d85ea2..1a756a3 100644
--- a/packages/flutter/lib/src/rendering/proxy_box.dart
+++ b/packages/flutter/lib/src/rendering/proxy_box.dart
@@ -2292,11 +2292,14 @@
     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 @@
   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 @@
       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);
     }
diff --git a/packages/flutter/lib/src/rendering/shifted_box.dart b/packages/flutter/lib/src/rendering/shifted_box.dart
index 832359c..a1a9776 100644
--- a/packages/flutter/lib/src/rendering/shifted_box.dart
+++ b/packages/flutter/lib/src/rendering/shifted_box.dart
@@ -626,8 +626,11 @@
     @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 @@
   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 @@
       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(() {
diff --git a/packages/flutter/lib/src/rendering/stack.dart b/packages/flutter/lib/src/rendering/stack.dart
index 5eacb7b..5977d22 100644
--- a/packages/flutter/lib/src/rendering/stack.dart
+++ b/packages/flutter/lib/src/rendering/stack.dart
@@ -268,6 +268,8 @@
   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 @@
     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 @@
     }
   }
 
-  /// 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 @@
 
   @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 @@
     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));
   }
 }
 
diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart
index 0424305..3fcc91a 100644
--- a/packages/flutter/lib/src/rendering/viewport.dart
+++ b/packages/flutter/lib/src/rendering/viewport.dart
@@ -171,17 +171,20 @@
     @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 @@
     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 @@
   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 @@
     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 @@
          offset: offset,
          cacheExtent: cacheExtent,
          cacheExtentStyle: cacheExtentStyle,
+         clipBehavior: clipBehavior,
        ) {
     addAll(children);
     if (center == null && firstChild != null)
@@ -1658,8 +1678,14 @@
     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);
   }
 
diff --git a/packages/flutter/lib/src/rendering/wrap.dart b/packages/flutter/lib/src/rendering/wrap.dart
index c1bb0d7..40359b5 100644
--- a/packages/flutter/lib/src/rendering/wrap.dart
+++ b/packages/flutter/lib/src/rendering/wrap.dart
@@ -101,8 +101,9 @@
 ///
 /// 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 @@
     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 @@
        _runSpacing = runSpacing,
        _crossAxisAlignment = crossAxisAlignment,
        _textDirection = textDirection,
-       _verticalDirection = verticalDirection {
+       _verticalDirection = verticalDirection,
+       _clipBehavior = clipBehavior {
     addAll(children);
   }
 
@@ -326,6 +330,20 @@
     }
   }
 
+  /// {@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 @@
   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);
   }
diff --git a/packages/flutter/lib/src/widgets/animated_cross_fade.dart b/packages/flutter/lib/src/widgets/animated_cross_fade.dart
index f23dd52..e1c563d 100644
--- a/packages/flutter/lib/src/widgets/animated_cross_fade.dart
+++ b/packages/flutter/lib/src/widgets/animated_cross_fade.dart
@@ -216,7 +216,7 @@
   /// [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,
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index de3530b..5c7e915 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -42,7 +42,6 @@
   MainAxisAlignment,
   MainAxisSize,
   MultiChildLayoutDelegate,
-  Overflow,
   PaintingContext,
   PointerCancelEvent,
   PointerCancelEventListener,
@@ -1411,9 +1410,11 @@
     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 @@
   ///    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 @@
     renderObject
       ..fit = fit
       ..alignment = alignment
-      ..textDirection = Directionality.of(context);
+      ..textDirection = Directionality.of(context)
+      ..clipBehavior = clipBehavior;
   }
 
   @override
@@ -2231,7 +2240,9 @@
     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 @@
   /// 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 @@
     textDirection: textDirection ?? Directionality.of(context),
     alignment: alignment,
     constrainedAxis: constrainedAxis,
+    clipBehavior: clipBehavior,
   );
 
   @override
@@ -3214,8 +3233,10 @@
     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 @@
   /// ([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 @@
       ..alignment = alignment
       ..textDirection = textDirection ?? Directionality.of(context)
       ..fit = fit
-      ..overflow = overflow;
+      ..clipBehavior = overflow == Overflow.clip ? Clip.hardEdge : clipBehavior;
   }
 
   @override
@@ -3283,7 +3314,7 @@
     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 @@
     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 @@
        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 @@
   /// 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 @@
       textDirection: getEffectiveTextDirection(context),
       verticalDirection: verticalDirection,
       textBaseline: textBaseline,
+      clipBehavior: clipBehavior,
     );
   }
 
@@ -3955,7 +3995,8 @@
       ..crossAxisAlignment = crossAxisAlignment
       ..textDirection = getEffectiveTextDirection(context)
       ..verticalDirection = verticalDirection
-      ..textBaseline = textBaseline;
+      ..textBaseline = textBaseline
+      ..clipBehavior = clipBehavior;
   }
 
   @override
@@ -4626,8 +4667,9 @@
     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 @@
   /// [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 @@
       crossAxisAlignment: crossAxisAlignment,
       textDirection: textDirection ?? Directionality.of(context),
       verticalDirection: verticalDirection,
+      clipBehavior: clipBehavior,
     );
   }
 
@@ -4785,7 +4834,8 @@
       ..runSpacing = runSpacing
       ..crossAxisAlignment = crossAxisAlignment
       ..textDirection = textDirection ?? Directionality.of(context)
-      ..verticalDirection = verticalDirection;
+      ..verticalDirection = verticalDirection
+      ..clipBehavior = clipBehavior;
   }
 
   @override
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
index ff29484..eae0a2c 100644
--- a/packages/flutter/lib/src/widgets/editable_text.dart
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
@@ -413,6 +413,7 @@
       selectAll: true,
     ),
     this.autofillHints,
+    this.clipBehavior = Clip.hardEdge,
   }) : assert(controller != null),
        assert(focusNode != null),
        assert(obscuringCharacter != null && obscuringCharacter.length == 1),
@@ -450,6 +451,7 @@
        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 @@
   /// {@endtemplate}
   final Iterable<String> autofillHints;
 
+  /// {@macro flutter.widgets.Clip}
+  ///
+  /// Defaults to [Clip.hardEdge].
+  final Clip clipBehavior;
+
   @override
   EditableTextState createState() => EditableTextState();
 
@@ -2089,6 +2096,7 @@
                 devicePixelRatio: _devicePixelRatio,
                 promptRectRange: _currentPromptRectRange,
                 promptRectColor: widget.autocorrectionTextRectColor,
+                clipBehavior: widget.clipBehavior,
               ),
             ),
           );
@@ -2163,6 +2171,7 @@
     this.devicePixelRatio,
     this.promptRectRange,
     this.promptRectColor,
+    this.clipBehavior,
   }) : assert(textDirection != null),
        assert(rendererIgnoresPointer != null),
        super(key: key);
@@ -2208,6 +2217,7 @@
   final double devicePixelRatio;
   final TextRange promptRectRange;
   final Color promptRectColor;
+  final Clip clipBehavior;
 
   @override
   RenderEditable createRenderObject(BuildContext context) {
@@ -2249,6 +2259,7 @@
       devicePixelRatio: devicePixelRatio,
       promptRectRange: promptRectRange,
       promptRectColor: promptRectColor,
+      clipBehavior: clipBehavior,
     );
   }
 
@@ -2289,6 +2300,7 @@
       ..devicePixelRatio = devicePixelRatio
       ..paintCursorAboveText = paintCursorAboveText
       ..promptRectColor = promptRectColor
+      ..clipBehavior = clipBehavior
       ..setPromptRectRange(promptRectRange);
   }
 }
diff --git a/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart b/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart
index 40fdc2b..49952a8 100644
--- a/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart
@@ -579,8 +579,8 @@
     @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 @@
        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 @@
     @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 @@
        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 @@
   /// 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 @@
             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 @@
   ///
   /// 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 @@
     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 @@
        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 @@
   /// 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 @@
   /// 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 @@
       overAndUnderCenterOpacity: overAndUnderCenterOpacity,
       itemExtent: itemExtent,
       squeeze: squeeze,
-      clipToSize: clipToSize,
       renderChildrenOutsideViewport: renderChildrenOutsideViewport,
+      clipBehavior: clipBehavior,
     );
   }
 
@@ -1064,7 +1068,7 @@
       ..overAndUnderCenterOpacity = overAndUnderCenterOpacity
       ..itemExtent = itemExtent
       ..squeeze = squeeze
-      ..clipToSize = clipToSize
-      ..renderChildrenOutsideViewport = renderChildrenOutsideViewport;
+      ..renderChildrenOutsideViewport = renderChildrenOutsideViewport
+      ..clipBehavior = clipBehavior;
   }
 }
diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart
index d552373..14f3e76 100644
--- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart
@@ -193,10 +193,12 @@
     @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 @@
   /// {@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 @@
               _lastHasScrolledBody,
             ),
             handle: _absorberHandle,
+            clipBehavior: widget.clipBehavior,
           );
         },
       ),
@@ -452,6 +460,7 @@
     @required ScrollController controller,
     @required List<Widget> slivers,
     @required this.handle,
+    @required this.clipBehavior,
     DragStartBehavior dragStartBehavior = DragStartBehavior.start,
   }) : super(
          scrollDirection: scrollDirection,
@@ -463,6 +472,7 @@
        );
 
   final SliverOverlapAbsorberHandle handle;
+  final Clip clipBehavior;
 
   @override
   Widget buildViewport(
@@ -477,6 +487,7 @@
       offset: offset,
       slivers: slivers,
       handle: handle,
+      clipBehavior: clipBehavior,
     );
   }
 }
@@ -1824,6 +1835,7 @@
     Key center,
     List<Widget> slivers = const <Widget>[],
     @required this.handle,
+    Clip clipBehavior = Clip.hardEdge,
   }) : assert(handle != null),
        super(
          key: key,
@@ -1833,6 +1845,7 @@
          offset: offset,
          center: center,
          slivers: slivers,
+         clipBehavior: clipBehavior,
        );
 
   /// The handle to the [SliverOverlapAbsorber] that is feeding this injector.
@@ -1849,6 +1862,7 @@
       anchor: anchor,
       offset: offset,
       handle: handle,
+      clipBehavior: clipBehavior,
     );
   }
 
@@ -1862,7 +1876,8 @@
       )
       ..anchor = anchor
       ..offset = offset
-      ..handle = handle;
+      ..handle = handle
+      ..clipBehavior = clipBehavior;
   }
 
   @override
@@ -1889,6 +1904,7 @@
     List<RenderSliver> children,
     RenderSliver center,
     @required SliverOverlapAbsorberHandle handle,
+    Clip clipBehavior = Clip.hardEdge,
   }) : assert(handle != null),
        _handle = handle,
        super(
@@ -1898,6 +1914,7 @@
          anchor: anchor,
          children: children,
          center: center,
+         clipBehavior: clipBehavior,
        );
 
   /// The object to notify when [markNeedsLayout] is called.
diff --git a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
index f4deca6..0d5b98a 100644
--- a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
@@ -219,8 +219,10 @@
     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 @@
   /// {@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 @@
           axisDirection: axisDirection,
           offset: offset,
           child: contents,
+          clipBehavior: clipBehavior,
         );
       },
     );
@@ -328,17 +336,21 @@
     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 @@
     // Order dependency: The offset setter reads the axis direction.
     renderObject
       ..axisDirection = axisDirection
-      ..offset = offset;
+      ..offset = offset
+      ..clipBehavior = clipBehavior;
   }
 }
 
@@ -357,12 +370,15 @@
     @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 @@
     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 @@
         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);
       }
diff --git a/packages/flutter/lib/src/widgets/viewport.dart b/packages/flutter/lib/src/widgets/viewport.dart
index 2623ad2..e478ec3 100644
--- a/packages/flutter/lib/src/widgets/viewport.dart
+++ b/packages/flutter/lib/src/widgets/viewport.dart
@@ -58,12 +58,14 @@
     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 @@
   /// {@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 @@
       offset: offset,
       cacheExtent: cacheExtent,
       cacheExtentStyle: cacheExtentStyle,
+      clipBehavior: clipBehavior,
     );
   }
 
@@ -158,7 +166,8 @@
       ..anchor = anchor
       ..offset = offset
       ..cacheExtent = cacheExtent
-      ..cacheExtentStyle = cacheExtentStyle;
+      ..cacheExtentStyle = cacheExtentStyle
+      ..clipBehavior = clipBehavior;
   }
 
   @override
@@ -263,6 +272,7 @@
     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 @@
   /// 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 @@
     renderObject
       ..axisDirection = axisDirection
       ..crossAxisDirection = crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection)
-      ..offset = offset;
+      ..offset = offset
+      ..clipBehavior = clipBehavior;
   }
 
   @override
diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart
index 1050e89..3a180bd 100644
--- a/packages/flutter/test/material/dropdown_test.dart
+++ b/packages/flutter/test/material/dropdown_test.dart
@@ -2143,8 +2143,8 @@
                           // 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,
diff --git a/packages/flutter/test/rendering/box_test.dart b/packages/flutter/test/rendering/box_test.dart
index bb4acbe..e0dcb784 100644
--- a/packages/flutter/test/rendering/box_test.dart
+++ b/packages/flutter/test/rendering/box_test.dart
@@ -670,6 +670,33 @@
     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());
diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart
index 48d793b..1614dea 100644
--- a/packages/flutter/test/rendering/editable_test.dart
+++ b/packages/flutter/test/rendering/editable_test.dart
@@ -24,6 +24,44 @@
 }
 
 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(
diff --git a/packages/flutter/test/rendering/flex_test.dart b/packages/flutter/test/rendering/flex_test.dart
index 824aff1..81ed36c 100644
--- a/packages/flutter/test/rendering/flex_test.dart
+++ b/packages/flutter/test/rendering/flex_test.dart
@@ -52,6 +52,24 @@
     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()
diff --git a/packages/flutter/test/rendering/proxy_box_test.dart b/packages/flutter/test/rendering/proxy_box_test.dart
index 6a49841..1d0d180 100644
--- a/packages/flutter/test/rendering/proxy_box_test.dart
+++ b/packages/flutter/test/rendering/proxy_box_test.dart
@@ -431,6 +431,7 @@
     _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 @@
     _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,
diff --git a/packages/flutter/test/rendering/rendering_tester.dart b/packages/flutter/test/rendering/rendering_tester.dart
index 6d24df8..cfd7236 100644
--- a/packages/flutter/test/rendering/rendering_tester.dart
+++ b/packages/flutter/test/rendering/rendering_tester.dart
@@ -294,3 +294,26 @@
     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));
diff --git a/packages/flutter/test/rendering/stack_test.dart b/packages/flutter/test/rendering/stack_test.dart
index 1243dc3..9449f0d 100644
--- a/packages/flutter/test/rendering/stack_test.dart
+++ b/packages/flutter/test/rendering/stack_test.dart
@@ -61,6 +61,33 @@
     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(
diff --git a/packages/flutter/test/rendering/wrap_test.dart b/packages/flutter/test/rendering/wrap_test.dart
index e6dac87..7667ff8 100644
--- a/packages/flutter/test/rendering/wrap_test.dart
+++ b/packages/flutter/test/rendering/wrap_test.dart
@@ -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 @@
     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));
+    }
+  });
 }
diff --git a/packages/flutter/test/widgets/basic_test.dart b/packages/flutter/test/widgets/basic_test.dart
index f1c502c..0c75008 100644
--- a/packages/flutter/test/widgets/basic_test.dart
+++ b/packages/flutter/test/widgets/basic_test.dart
@@ -280,6 +280,15 @@
     );
   });
 
+  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;
diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart
index 05c4c8e..7a3e894 100644
--- a/packages/flutter/test/widgets/editable_text_test.dart
+++ b/packages/flutter/test/widgets/editable_text_test.dart
@@ -4761,6 +4761,48 @@
 
     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 {
diff --git a/packages/flutter/test/widgets/fitted_box_test.dart b/packages/flutter/test/widgets/fitted_box_test.dart
index 0469a5a..7d47d6e 100644
--- a/packages/flutter/test/widgets/fitted_box_test.dart
+++ b/packages/flutter/test/widgets/fitted_box_test.dart
@@ -369,6 +369,7 @@
           height: 10.0,
           child: FittedBox(
             fit: BoxFit.cover,
+            clipBehavior: Clip.hardEdge,
             child: SizedBox(
               width: 10.0,
               height: 50.0,
@@ -391,6 +392,7 @@
           height: 100.0,
           child: FittedBox(
             fit: BoxFit.cover,
+            clipBehavior: Clip.hardEdge,
             child: SizedBox(
               width: 50.0,
               height: 10.0,
@@ -418,6 +420,7 @@
                   height: b,
                   child: FittedBox(
                     fit: BoxFit.none,
+                    clipBehavior: Clip.hardEdge,
                     child: SizedBox(
                       width: c,
                       height: d,
@@ -472,6 +475,15 @@
     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() {
diff --git a/packages/flutter/test/widgets/flex_test.dart b/packages/flutter/test/widgets/flex_test.dart
index 993dc7e..a249a7e 100644
--- a/packages/flutter/test/widgets/flex_test.dart
+++ b/packages/flutter/test/widgets/flex_test.dart
@@ -140,4 +140,13 @@
     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));
+  });
 }
diff --git a/packages/flutter/test/widgets/framework_test.dart b/packages/flutter/test/widgets/framework_test.dart
index 68bb488..7586da7 100644
--- a/packages/flutter/test/widgets/framework_test.dart
+++ b/packages/flutter/test/widgets/framework_test.dart
@@ -719,7 +719,7 @@
       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 @@
         '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.'
       ),
     );
diff --git a/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart b/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
index 4de089f..f26e307 100644
--- a/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
@@ -11,6 +11,44 @@
 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 {
diff --git a/packages/flutter/test/widgets/nested_scroll_view_test.dart b/packages/flutter/test/widgets/nested_scroll_view_test.dart
index c01c311..e40ea07 100644
--- a/packages/flutter/test/widgets/nested_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/nested_scroll_view_test.dart
@@ -8,6 +8,8 @@
 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 @@
 }
 
 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);
diff --git a/packages/flutter/test/widgets/shrink_wrapping_viewport_test.dart b/packages/flutter/test/widgets/shrink_wrapping_viewport_test.dart
new file mode 100644
index 0000000..f377e11
--- /dev/null
+++ b/packages/flutter/test/widgets/shrink_wrapping_viewport_test.dart
@@ -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));
+  });
+}
diff --git a/packages/flutter/test/widgets/single_child_scroll_view_test.dart b/packages/flutter/test/widgets/single_child_scroll_view_test.dart
index be96c1c..43dd611 100644
--- a/packages/flutter/test/widgets/single_child_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/single_child_scroll_view_test.dart
@@ -8,6 +8,7 @@
 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 @@
 }
 
 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(
diff --git a/packages/flutter/test/widgets/stack_test.dart b/packages/flutter/test/widgets/stack_test.dart
index 7447140..878ac4a 100644
--- a/packages/flutter/test/widgets/stack_test.dart
+++ b/packages/flutter/test/widgets/stack_test.dart
@@ -376,6 +376,15 @@
     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 @@
         textDirection: TextDirection.ltr,
         child: Center(
           child: Stack(
+            clipBehavior: Clip.hardEdge,
             children: const <Widget>[
               SizedBox(
                 width: 100.0,
@@ -442,6 +452,7 @@
         child: Center(
           child: Stack(
             overflow: Overflow.visible,
+            clipBehavior: Clip.none,
             children: const <Widget>[
               SizedBox(
                 width: 100.0,
diff --git a/packages/flutter/test/widgets/wrap_test.dart b/packages/flutter/test/widgets/wrap_test.dart
index 46b7b83..d94884d 100644
--- a/packages/flutter/test/widgets/wrap_test.dart
+++ b/packages/flutter/test/widgets/wrap_test.dart
@@ -733,6 +733,7 @@
 
     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 @@
       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));
+  });
 }