Unverified Commit 13bfa735 authored by liyuqian's avatar liyuqian Committed by GitHub

Set default clipBehavior to Clip.none and update tests (#20205)

This fixes https://github.com/flutter/flutter/issues/18057
parent ace8abf3
...@@ -29,8 +29,8 @@ import 'theme_data.dart'; ...@@ -29,8 +29,8 @@ import 'theme_data.dart';
class RawMaterialButton extends StatefulWidget { class RawMaterialButton extends StatefulWidget {
/// Create a button based on [Semantics], [Material], and [InkWell] widgets. /// Create a button based on [Semantics], [Material], and [InkWell] widgets.
/// ///
/// The [shape], [elevation], [padding], and [constraints] arguments /// The [shape], [elevation], [padding], [constraints], and [clipBehavior]
/// must not be null. /// arguments must not be null.
const RawMaterialButton({ const RawMaterialButton({
Key key, Key key,
@required this.onPressed, @required this.onPressed,
...@@ -46,6 +46,7 @@ class RawMaterialButton extends StatefulWidget { ...@@ -46,6 +46,7 @@ class RawMaterialButton extends StatefulWidget {
this.constraints = const BoxConstraints(minWidth: 88.0, minHeight: 36.0), this.constraints = const BoxConstraints(minWidth: 88.0, minHeight: 36.0),
this.shape = const RoundedRectangleBorder(), this.shape = const RoundedRectangleBorder(),
this.animationDuration = kThemeChangeDuration, this.animationDuration = kThemeChangeDuration,
this.clipBehavior = Clip.none,
MaterialTapTargetSize materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
this.child, this.child,
}) : this.materialTapTargetSize = materialTapTargetSize ?? MaterialTapTargetSize.padded, }) : this.materialTapTargetSize = materialTapTargetSize ?? MaterialTapTargetSize.padded,
...@@ -56,6 +57,7 @@ class RawMaterialButton extends StatefulWidget { ...@@ -56,6 +57,7 @@ class RawMaterialButton extends StatefulWidget {
assert(padding != null), assert(padding != null),
assert(constraints != null), assert(constraints != null),
assert(animationDuration != null), assert(animationDuration != null),
assert(clipBehavior != null),
super(key: key); super(key: key);
/// Called when the button is tapped or otherwise activated. /// Called when the button is tapped or otherwise activated.
...@@ -148,6 +150,9 @@ class RawMaterialButton extends StatefulWidget { ...@@ -148,6 +150,9 @@ class RawMaterialButton extends StatefulWidget {
/// * [MaterialTapTargetSize], for a description of how this affects tap targets. /// * [MaterialTapTargetSize], for a description of how this affects tap targets.
final MaterialTapTargetSize materialTapTargetSize; final MaterialTapTargetSize materialTapTargetSize;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
@override @override
_RawMaterialButtonState createState() => new _RawMaterialButtonState(); _RawMaterialButtonState createState() => new _RawMaterialButtonState();
} }
...@@ -177,6 +182,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> { ...@@ -177,6 +182,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
color: widget.fillColor, color: widget.fillColor,
type: widget.fillColor == null ? MaterialType.transparency : MaterialType.button, type: widget.fillColor == null ? MaterialType.transparency : MaterialType.button,
animationDuration: widget.animationDuration, animationDuration: widget.animationDuration,
clipBehavior: widget.clipBehavior,
child: new InkWell( child: new InkWell(
onHighlightChanged: _handleHighlightChanged, onHighlightChanged: _handleHighlightChanged,
splashColor: widget.splashColor, splashColor: widget.splashColor,
...@@ -245,6 +251,8 @@ class MaterialButton extends StatelessWidget { ...@@ -245,6 +251,8 @@ class MaterialButton extends StatelessWidget {
/// Rather than creating a material button directly, consider using /// Rather than creating a material button directly, consider using
/// [FlatButton] or [RaisedButton]. To create a custom Material button /// [FlatButton] or [RaisedButton]. To create a custom Material button
/// consider using [RawMaterialButton]. /// consider using [RawMaterialButton].
///
/// The [clipBehavior] argument must not be null.
const MaterialButton({ const MaterialButton({
Key key, Key key,
this.colorBrightness, this.colorBrightness,
...@@ -259,9 +267,10 @@ class MaterialButton extends StatelessWidget { ...@@ -259,9 +267,10 @@ class MaterialButton extends StatelessWidget {
this.height, this.height,
this.padding, this.padding,
this.materialTapTargetSize, this.materialTapTargetSize,
this.clipBehavior = Clip.none,
@required this.onPressed, @required this.onPressed,
this.child this.child
}) : super(key: key); }) : assert(clipBehavior != null), super(key: key);
/// The theme brightness to use for this button. /// The theme brightness to use for this button.
/// ///
...@@ -373,6 +382,9 @@ class MaterialButton extends StatelessWidget { ...@@ -373,6 +382,9 @@ class MaterialButton extends StatelessWidget {
/// * [MaterialTapTargetSize], for a description of how this affects tap targets. /// * [MaterialTapTargetSize], for a description of how this affects tap targets.
final MaterialTapTargetSize materialTapTargetSize; final MaterialTapTargetSize materialTapTargetSize;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// Whether the button is enabled or disabled. Buttons are disabled by default. To /// Whether the button is enabled or disabled. Buttons are disabled by default. To
/// enable a button, set its [onPressed] property to a non-null value. /// enable a button, set its [onPressed] property to a non-null value.
bool get enabled => onPressed != null; bool get enabled => onPressed != null;
...@@ -433,6 +445,7 @@ class MaterialButton extends StatelessWidget { ...@@ -433,6 +445,7 @@ class MaterialButton extends StatelessWidget {
shape: buttonTheme.shape, shape: buttonTheme.shape,
child: child, child: child,
materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize, materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize,
clipBehavior: clipBehavior,
); );
} }
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -174,7 +172,7 @@ class Material extends StatefulWidget { ...@@ -174,7 +172,7 @@ class Material extends StatefulWidget {
this.textStyle, this.textStyle,
this.borderRadius, this.borderRadius,
this.shape, this.shape,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use this.clipBehavior = Clip.none,
this.animationDuration = kThemeChangeDuration, this.animationDuration = kThemeChangeDuration,
this.child, this.child,
}) : assert(type != null), }) : assert(type != null),
...@@ -370,14 +368,16 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin { ...@@ -370,14 +368,16 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
} }
static Widget _transparentInterior({ShapeBorder shape, Clip clipBehavior, Widget contents}) { static Widget _transparentInterior({ShapeBorder shape, Clip clipBehavior, Widget contents}) {
final _ShapeBorderPaint child = new _ShapeBorderPaint(
child: contents,
shape: shape,
);
if (clipBehavior == Clip.none) {
return child;
}
return new ClipPath( return new ClipPath(
child: new _ShapeBorderPaint( child: child,
child: contents, clipper: new ShapeBorderClipper(shape: shape),
shape: shape,
),
clipper: new ShapeBorderClipper(
shape: shape,
),
clipBehavior: clipBehavior, clipBehavior: clipBehavior,
); );
} }
...@@ -598,7 +598,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget { ...@@ -598,7 +598,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
Key key, Key key,
@required this.child, @required this.child,
@required this.shape, @required this.shape,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use this.clipBehavior = Clip.none,
@required this.elevation, @required this.elevation,
@required this.color, @required this.color,
@required this.shadowColor, @required this.shadowColor,
......
...@@ -51,7 +51,7 @@ const Duration _kElevationDuration = Duration(milliseconds: 75); ...@@ -51,7 +51,7 @@ const Duration _kElevationDuration = Duration(milliseconds: 75);
class OutlineButton extends StatefulWidget { class OutlineButton extends StatefulWidget {
/// Create a filled button. /// Create a filled button.
/// ///
/// The [highlightElevation], and [borderWidth] /// The [highlightElevation], [borderWidth], and [clipBehavior]
/// arguments must not be null. /// arguments must not be null.
const OutlineButton({ const OutlineButton({
Key key, Key key,
...@@ -68,8 +68,10 @@ class OutlineButton extends StatefulWidget { ...@@ -68,8 +68,10 @@ class OutlineButton extends StatefulWidget {
this.highlightedBorderColor, this.highlightedBorderColor,
this.padding, this.padding,
this.shape, this.shape,
this.clipBehavior = Clip.none,
this.child, this.child,
}) : assert(highlightElevation != null && highlightElevation >= 0.0), }) : assert(highlightElevation != null && highlightElevation >= 0.0),
assert(clipBehavior != null),
super(key: key); super(key: key);
/// Create an outline button from a pair of widgets that serve as the button's /// Create an outline button from a pair of widgets that serve as the button's
...@@ -78,7 +80,8 @@ class OutlineButton extends StatefulWidget { ...@@ -78,7 +80,8 @@ class OutlineButton extends StatefulWidget {
/// The icon and label are arranged in a row and padded by 12 logical pixels /// The icon and label are arranged in a row and padded by 12 logical pixels
/// at the start, and 16 at the end, with an 8 pixel gap in between. /// at the start, and 16 at the end, with an 8 pixel gap in between.
/// ///
/// The [highlightElevation], [icon], and [label] must not be null. /// The [highlightElevation], [icon], [label], and [clipBehavior] must not be
/// null.
OutlineButton.icon({ OutlineButton.icon({
Key key, Key key,
@required this.onPressed, @required this.onPressed,
...@@ -93,11 +96,13 @@ class OutlineButton extends StatefulWidget { ...@@ -93,11 +96,13 @@ class OutlineButton extends StatefulWidget {
this.disabledBorderColor, this.disabledBorderColor,
this.highlightedBorderColor, this.highlightedBorderColor,
this.shape, this.shape,
this.clipBehavior = Clip.none,
@required Widget icon, @required Widget icon,
@required Widget label, @required Widget label,
}) : assert(highlightElevation != null && highlightElevation >= 0.0), }) : assert(highlightElevation != null && highlightElevation >= 0.0),
assert(icon != null), assert(icon != null),
assert(label != null), assert(label != null),
assert(clipBehavior != null),
padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0), padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0),
child = new Row( child = new Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
...@@ -223,6 +228,9 @@ class OutlineButton extends StatefulWidget { ...@@ -223,6 +228,9 @@ class OutlineButton extends StatefulWidget {
/// shape as well. /// shape as well.
final ShapeBorder shape; final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// The button's label. /// The button's label.
/// ///
/// Often a [Text] widget in all caps. /// Often a [Text] widget in all caps.
...@@ -415,6 +423,7 @@ class _OutlineButtonState extends State<OutlineButton> with SingleTickerProvider ...@@ -415,6 +423,7 @@ class _OutlineButtonState extends State<OutlineButton> with SingleTickerProvider
shape: widget.shape ?? buttonTheme.shape, shape: widget.shape ?? buttonTheme.shape,
side: _getOutline(theme, buttonTheme), side: _getOutline(theme, buttonTheme),
), ),
clipBehavior: widget.clipBehavior,
animationDuration: _kElevationDuration, animationDuration: _kElevationDuration,
child: widget.child, child: widget.child,
); );
......
...@@ -44,8 +44,8 @@ import 'theme_data.dart'; ...@@ -44,8 +44,8 @@ import 'theme_data.dart';
class RaisedButton extends StatelessWidget { class RaisedButton extends StatelessWidget {
/// Create a filled button. /// Create a filled button.
/// ///
/// The [elevation], [highlightElevation], and [disabledElevation] /// The [elevation], [highlightElevation], [disabledElevation], and
/// arguments must not be null. /// [clipBehavior] arguments must not be null.
const RaisedButton({ const RaisedButton({
Key key, Key key,
@required this.onPressed, @required this.onPressed,
...@@ -63,6 +63,7 @@ class RaisedButton extends StatelessWidget { ...@@ -63,6 +63,7 @@ class RaisedButton extends StatelessWidget {
this.disabledElevation = 0.0, this.disabledElevation = 0.0,
this.padding, this.padding,
this.shape, this.shape,
this.clipBehavior = Clip.none,
this.materialTapTargetSize, this.materialTapTargetSize,
this.animationDuration = kThemeChangeDuration, this.animationDuration = kThemeChangeDuration,
this.child, this.child,
...@@ -70,6 +71,7 @@ class RaisedButton extends StatelessWidget { ...@@ -70,6 +71,7 @@ class RaisedButton extends StatelessWidget {
assert(highlightElevation != null), assert(highlightElevation != null),
assert(disabledElevation != null), assert(disabledElevation != null),
assert(animationDuration != null), assert(animationDuration != null),
assert(clipBehavior != null),
super(key: key); super(key: key);
/// Create a filled button from a pair of widgets that serve as the button's /// Create a filled button from a pair of widgets that serve as the button's
...@@ -78,8 +80,8 @@ class RaisedButton extends StatelessWidget { ...@@ -78,8 +80,8 @@ class RaisedButton extends StatelessWidget {
/// The icon and label are arranged in a row and padded by 12 logical pixels /// The icon and label are arranged in a row and padded by 12 logical pixels
/// at the start, and 16 at the end, with an 8 pixel gap in between. /// at the start, and 16 at the end, with an 8 pixel gap in between.
/// ///
/// The [elevation], [highlightElevation], [disabledElevation], [icon], and /// The [elevation], [highlightElevation], [disabledElevation], [icon],
/// [label] arguments must not be null. /// [label], and [clipBehavior] arguments must not be null.
RaisedButton.icon({ RaisedButton.icon({
Key key, Key key,
@required this.onPressed, @required this.onPressed,
...@@ -96,6 +98,7 @@ class RaisedButton extends StatelessWidget { ...@@ -96,6 +98,7 @@ class RaisedButton extends StatelessWidget {
this.highlightElevation = 8.0, this.highlightElevation = 8.0,
this.disabledElevation = 0.0, this.disabledElevation = 0.0,
this.shape, this.shape,
this.clipBehavior = Clip.none,
this.materialTapTargetSize, this.materialTapTargetSize,
this.animationDuration = kThemeChangeDuration, this.animationDuration = kThemeChangeDuration,
@required Widget icon, @required Widget icon,
...@@ -106,6 +109,7 @@ class RaisedButton extends StatelessWidget { ...@@ -106,6 +109,7 @@ class RaisedButton extends StatelessWidget {
assert(icon != null), assert(icon != null),
assert(label != null), assert(label != null),
assert(animationDuration != null), assert(animationDuration != null),
assert(clipBehavior != null),
padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0), padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0),
child = new Row( child = new Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
...@@ -287,6 +291,9 @@ class RaisedButton extends StatelessWidget { ...@@ -287,6 +291,9 @@ class RaisedButton extends StatelessWidget {
/// shape as well. /// shape as well.
final ShapeBorder shape; final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// Defines the duration of animated changes for [shape] and [elevation]. /// Defines the duration of animated changes for [shape] and [elevation].
/// ///
/// The default value is [kThemeChangeDuration]. /// The default value is [kThemeChangeDuration].
...@@ -390,6 +397,7 @@ class RaisedButton extends StatelessWidget { ...@@ -390,6 +397,7 @@ class RaisedButton extends StatelessWidget {
padding: padding ?? buttonTheme.padding, padding: padding ?? buttonTheme.padding,
constraints: buttonTheme.constraints, constraints: buttonTheme.constraints,
shape: shape ?? buttonTheme.shape, shape: shape ?? buttonTheme.shape,
clipBehavior: clipBehavior,
animationDuration: animationDuration, animationDuration: animationDuration,
child: child, child: child,
materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize, materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize,
......
...@@ -8,6 +8,7 @@ export 'dart:ui' show ...@@ -8,6 +8,7 @@ export 'dart:ui' show
BlendMode, BlendMode,
BlurStyle, BlurStyle,
Canvas, Canvas,
Clip,
Color, Color,
ColorFilter, ColorFilter,
FilterQuality, FilterQuality,
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:ui' as ui show Image, ImageFilter, Picture, Scene, SceneBuilder; import 'dart:ui' as ui show Image, ImageFilter, Picture, Scene, SceneBuilder;
import 'dart:ui' show Clip, Offset, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
...@@ -626,7 +625,11 @@ class ClipRectLayer extends ContainerLayer { ...@@ -626,7 +625,11 @@ class ClipRectLayer extends ContainerLayer {
/// (as described at [Layer]). /// (as described at [Layer]).
Rect clipRect; Rect clipRect;
/// {@macro flutter.clipper.clipBehavior} /// {@template flutter.clipper.clipBehavior}
/// Controls how to clip (default to [Clip.antiAlias]).
///
/// [Clip.none] is not allowed here.
/// {@endtemplate}
Clip get clipBehavior => _clipBehavior; Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior; Clip _clipBehavior;
set clipBehavior(Clip value) { set clipBehavior(Clip value) {
...@@ -967,7 +970,7 @@ class PhysicalModelLayer extends ContainerLayer { ...@@ -967,7 +970,7 @@ class PhysicalModelLayer extends ContainerLayer {
/// The [clipPath], [elevation], and [color] arguments must not be null. /// The [clipPath], [elevation], and [color] arguments must not be null.
PhysicalModelLayer({ PhysicalModelLayer({
@required this.clipPath, @required this.clipPath,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use this.clipBehavior = Clip.none,
@required this.elevation, @required this.elevation,
@required this.color, @required this.color,
@required this.shadowColor, @required this.shadowColor,
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:ui' as ui show PictureRecorder; import 'dart:ui' as ui show PictureRecorder;
import 'dart:ui' show Clip;
import 'package:flutter/animation.dart'; import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui show ImageFilter, Gradient, Image; import 'dart:ui' as ui show ImageFilter, Gradient, Image;
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/animation.dart'; import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -1118,8 +1117,8 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox { ...@@ -1118,8 +1117,8 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox {
_RenderCustomClip({ _RenderCustomClip({
RenderBox child, RenderBox child,
CustomClipper<T> clipper, CustomClipper<T> clipper,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use this.clipBehavior = Clip.antiAlias,
}) : _clipper = clipper, assert(clipBehavior != null), assert(clipBehavior != Clip.none), super(child); }) : _clipper = clipper, assert(clipBehavior != null), super(child);
/// If non-null, determines which clip to use on the child. /// If non-null, determines which clip to use on the child.
CustomClipper<T> get clipper => _clipper; CustomClipper<T> get clipper => _clipper;
...@@ -1162,11 +1161,6 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox { ...@@ -1162,11 +1161,6 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox {
T get _defaultClip; T get _defaultClip;
T _clip; T _clip;
/// {@template flutter.clipper.clipBehavior}
/// Controls how to clip (default to [Clip.antiAlias]).
///
/// [Clip.none] is not allowed here.
/// {@endtemplate}
final Clip clipBehavior; final Clip clipBehavior;
@override @override
...@@ -1227,6 +1221,8 @@ class RenderClipRect extends _RenderCustomClip<Rect> { ...@@ -1227,6 +1221,8 @@ class RenderClipRect extends _RenderCustomClip<Rect> {
/// ///
/// If [clipper] is null, the clip will match the layout size and position of /// If [clipper] is null, the clip will match the layout size and position of
/// the child. /// the child.
///
/// The [clipBehavior] cannot be [Clip.none].
RenderClipRect({ RenderClipRect({
RenderBox child, RenderBox child,
CustomClipper<Rect> clipper, CustomClipper<Rect> clipper,
...@@ -1280,12 +1276,14 @@ class RenderClipRRect extends _RenderCustomClip<RRect> { ...@@ -1280,12 +1276,14 @@ class RenderClipRRect extends _RenderCustomClip<RRect> {
/// right-angled corners. /// right-angled corners.
/// ///
/// If [clipper] is non-null, then [borderRadius] is ignored. /// If [clipper] is non-null, then [borderRadius] is ignored.
///
/// The [clipBehavior] cannot be [Clip.none].
RenderClipRRect({ RenderClipRRect({
RenderBox child, RenderBox child,
BorderRadius borderRadius = BorderRadius.zero, BorderRadius borderRadius = BorderRadius.zero,
CustomClipper<RRect> clipper, CustomClipper<RRect> clipper,
Clip clipBehavior = Clip.antiAlias, Clip clipBehavior = Clip.antiAlias,
}) : _borderRadius = borderRadius, super(child: child, clipper: clipper, clipBehavior: clipBehavior) { }) : assert(clipBehavior != Clip.none), _borderRadius = borderRadius, super(child: child, clipper: clipper, clipBehavior: clipBehavior) {
assert(_borderRadius != null || clipper != null); assert(_borderRadius != null || clipper != null);
} }
...@@ -1350,11 +1348,13 @@ class RenderClipOval extends _RenderCustomClip<Rect> { ...@@ -1350,11 +1348,13 @@ class RenderClipOval extends _RenderCustomClip<Rect> {
/// ///
/// If [clipper] is null, the oval will be inscribed into the layout size and /// If [clipper] is null, the oval will be inscribed into the layout size and
/// position of the child. /// position of the child.
///
/// The [clipBehavior] cannot be [Clip.none].
RenderClipOval({ RenderClipOval({
RenderBox child, RenderBox child,
CustomClipper<Rect> clipper, CustomClipper<Rect> clipper,
Clip clipBehavior = Clip.antiAlias, Clip clipBehavior = Clip.antiAlias,
}) : super(child: child, clipper: clipper, clipBehavior: clipBehavior); }) : assert(clipBehavior != Clip.none), super(child: child, clipper: clipper, clipBehavior: clipBehavior);
Rect _cachedRect; Rect _cachedRect;
Path _cachedPath; Path _cachedPath;
...@@ -1423,11 +1423,13 @@ class RenderClipPath extends _RenderCustomClip<Path> { ...@@ -1423,11 +1423,13 @@ class RenderClipPath extends _RenderCustomClip<Path> {
/// size and location of the child. However, rather than use this default, /// size and location of the child. However, rather than use this default,
/// consider using a [RenderClipRect], which can achieve the same effect more /// consider using a [RenderClipRect], which can achieve the same effect more
/// efficiently. /// efficiently.
///
/// The [clipBehavior] cannot be [Clip.none].
RenderClipPath({ RenderClipPath({
RenderBox child, RenderBox child,
CustomClipper<Path> clipper, CustomClipper<Path> clipper,
Clip clipBehavior = Clip.antiAlias, Clip clipBehavior = Clip.antiAlias,
}) : super(child: child, clipper: clipper, clipBehavior: clipBehavior); }) : assert(clipBehavior != Clip.none), super(child: child, clipper: clipper, clipBehavior: clipBehavior);
@override @override
Path get _defaultClip => new Path()..addRect(Offset.zero & size); Path get _defaultClip => new Path()..addRect(Offset.zero & size);
...@@ -1475,7 +1477,7 @@ abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> { ...@@ -1475,7 +1477,7 @@ abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> {
@required double elevation, @required double elevation,
@required Color color, @required Color color,
@required Color shadowColor, @required Color shadowColor,
Clip clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use Clip clipBehavior = Clip.none,
CustomClipper<T> clipper, CustomClipper<T> clipper,
}) : assert(elevation != null), }) : assert(elevation != null),
assert(color != null), assert(color != null),
...@@ -1554,7 +1556,7 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> { ...@@ -1554,7 +1556,7 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
RenderPhysicalModel({ RenderPhysicalModel({
RenderBox child, RenderBox child,
BoxShape shape = BoxShape.rectangle, BoxShape shape = BoxShape.rectangle,
Clip clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use Clip clipBehavior = Clip.none,
BorderRadius borderRadius, BorderRadius borderRadius,
double elevation = 0.0, double elevation = 0.0,
@required Color color, @required Color color,
...@@ -1713,7 +1715,7 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> { ...@@ -1713,7 +1715,7 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
RenderPhysicalShape({ RenderPhysicalShape({
RenderBox child, RenderBox child,
@required CustomClipper<Path> clipper, @required CustomClipper<Path> clipper,
Clip clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use Clip clipBehavior = Clip.none,
double elevation = 0.0, double elevation = 0.0,
@required Color color, @required Color color,
Color shadowColor = const Color(0xFF000000), Color shadowColor = const Color(0xFF000000),
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' as ui show Image, ImageFilter; import 'dart:ui' as ui show Image, ImageFilter;
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -692,7 +691,7 @@ class PhysicalModel extends SingleChildRenderObjectWidget { ...@@ -692,7 +691,7 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
const PhysicalModel({ const PhysicalModel({
Key key, Key key,
this.shape = BoxShape.rectangle, this.shape = BoxShape.rectangle,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use this.clipBehavior = Clip.none,
this.borderRadius, this.borderRadius,
this.elevation = 0.0, this.elevation = 0.0,
@required this.color, @required this.color,
...@@ -780,7 +779,7 @@ class PhysicalShape extends SingleChildRenderObjectWidget { ...@@ -780,7 +779,7 @@ class PhysicalShape extends SingleChildRenderObjectWidget {
const PhysicalShape({ const PhysicalShape({
Key key, Key key,
@required this.clipper, @required this.clipper,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use this.clipBehavior = Clip.none,
this.elevation = 0.0, this.elevation = 0.0,
@required this.color, @required this.color,
this.shadowColor = const Color(0xFF000000), this.shadowColor = const Color(0xFF000000),
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
import 'package:flutter/animation.dart'; import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -1183,7 +1181,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget { ...@@ -1183,7 +1181,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
Key key, Key key,
@required this.child, @required this.child,
@required this.shape, @required this.shape,
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use this.clipBehavior = Clip.none,
this.borderRadius = BorderRadius.zero, this.borderRadius = BorderRadius.zero,
@required this.elevation, @required this.elevation,
@required this.color, @required this.color,
......
...@@ -179,6 +179,7 @@ void main() { ...@@ -179,6 +179,7 @@ void main() {
splashColor: directSplashColor, splashColor: directSplashColor,
highlightColor: directHighlightColor, highlightColor: directHighlightColor,
onPressed: () { /* to make sure the button is enabled */ }, onPressed: () { /* to make sure the button is enabled */ },
clipBehavior: Clip.antiAlias,
), ),
), ),
); );
...@@ -224,6 +225,7 @@ void main() { ...@@ -224,6 +225,7 @@ void main() {
child: new Center( child: new Center(
child: new MaterialButton( child: new MaterialButton(
onPressed: () { /* to make sure the button is enabled */ }, onPressed: () { /* to make sure the button is enabled */ },
clipBehavior: Clip.antiAlias,
), ),
), ),
); );
...@@ -280,6 +282,35 @@ void main() { ...@@ -280,6 +282,35 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('MaterialButton has no clip by default', (WidgetTester tester) async {
final GlobalKey buttonKey = new GlobalKey();
final Widget buttonWidget = new Material(
child: new Center(
child: new MaterialButton(
key: buttonKey,
onPressed: () { /* to make sure the button is enabled */ },
),
),
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Theme(
data: new ThemeData(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: buttonWidget,
),
),
);
expect(
tester.renderObject(find.byKey(buttonKey)),
paintsExactlyCountTimes(#clipPath, 0)
);
});
testWidgets('Disabled MaterialButton has same semantic size as enabled and exposes disabled semantics', (WidgetTester tester) async { testWidgets('Disabled MaterialButton has same semantic size as enabled and exposes disabled semantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
......
...@@ -171,27 +171,42 @@ void main() { ...@@ -171,27 +171,42 @@ void main() {
}); });
group('Transparency clipping', () { group('Transparency clipping', () {
testWidgets('clips to bounding rect by default', (WidgetTester tester) async { testWidgets('No clip by default', (WidgetTester tester) async {
final GlobalKey materialKey = new GlobalKey();
await tester.pumpWidget(
new Material(
key: materialKey,
type: MaterialType.transparency,
child: const SizedBox(width: 100.0, height: 100.0),
)
);
expect(find.byKey(materialKey), hasNoImmediateClip);
});
testWidgets('clips to bounding rect by default given Clip.antiAlias', (WidgetTester tester) async {
final GlobalKey materialKey = new GlobalKey(); final GlobalKey materialKey = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new Material( new Material(
key: materialKey, key: materialKey,
type: MaterialType.transparency, type: MaterialType.transparency,
child: const SizedBox(width: 100.0, height: 100.0) child: const SizedBox(width: 100.0, height: 100.0),
clipBehavior: Clip.antiAlias,
) )
); );
expect(find.byKey(materialKey), clipsWithBoundingRect); expect(find.byKey(materialKey), clipsWithBoundingRect);
}); });
testWidgets('clips to rounded rect when borderRadius provided', (WidgetTester tester) async { testWidgets('clips to rounded rect when borderRadius provided given Clip.antiAlias', (WidgetTester tester) async {
final GlobalKey materialKey = new GlobalKey(); final GlobalKey materialKey = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new Material( new Material(
key: materialKey, key: materialKey,
type: MaterialType.transparency, type: MaterialType.transparency,
borderRadius: const BorderRadius.all(Radius.circular(10.0)), borderRadius: const BorderRadius.all(Radius.circular(10.0)),
child: const SizedBox(width: 100.0, height: 100.0) child: const SizedBox(width: 100.0, height: 100.0),
clipBehavior: Clip.antiAlias,
) )
); );
...@@ -203,14 +218,15 @@ void main() { ...@@ -203,14 +218,15 @@ void main() {
); );
}); });
testWidgets('clips to shape when provided', (WidgetTester tester) async { testWidgets('clips to shape when provided given Clip.antiAlias', (WidgetTester tester) async {
final GlobalKey materialKey = new GlobalKey(); final GlobalKey materialKey = new GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
new Material( new Material(
key: materialKey, key: materialKey,
type: MaterialType.transparency, type: MaterialType.transparency,
shape: const StadiumBorder(), shape: const StadiumBorder(),
child: const SizedBox(width: 100.0, height: 100.0) child: const SizedBox(width: 100.0, height: 100.0),
clipBehavior: Clip.antiAlias,
) )
); );
......
...@@ -60,6 +60,7 @@ void main() { ...@@ -60,6 +60,7 @@ void main() {
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: OutlineButton( child: OutlineButton(
shape: const RoundedRectangleBorder(), // default border radius is 0 shape: const RoundedRectangleBorder(), // default border radius is 0
clipBehavior: Clip.antiAlias,
color: fillColor, color: fillColor,
highlightedBorderColor: highlightedBorderColor, highlightedBorderColor: highlightedBorderColor,
disabledBorderColor: disabledBorderColor, disabledBorderColor: disabledBorderColor,
...@@ -135,6 +136,29 @@ void main() { ...@@ -135,6 +136,29 @@ void main() {
..path(color: borderColor, strokeWidth: borderWidth)); ..path(color: borderColor, strokeWidth: borderWidth));
}); });
testWidgets('OutlineButton has no clip by default', (WidgetTester tester) async {
final GlobalKey buttonKey = new GlobalKey();
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Material(
child: new Center(
child: new OutlineButton(
key: buttonKey,
onPressed: () { },
child: const Text('ABC'),
),
),
),
),
);
expect(
tester.renderObject(find.byKey(buttonKey)),
paintsExactlyCountTimes(#clipPath, 0)
);
});
testWidgets('OutlineButton contributes semantics', (WidgetTester tester) async { testWidgets('OutlineButton contributes semantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget( await tester.pumpWidget(
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show Clip;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
......
...@@ -431,7 +431,70 @@ void main() { ...@@ -431,7 +431,70 @@ void main() {
); );
}); });
testWidgets('PhysicalModel painting', (WidgetTester tester) async { Center genPhysicalModel(Clip clipBehavior) {
return new Center(
child: new RepaintBoundary(
child: new Container(
color: Colors.white,
child: new Padding(
padding: const EdgeInsets.all(100.0),
child: new SizedBox(
height: 100.0,
width: 100.0,
child: new Transform.rotate(
angle: 1.0, // radians
child: new PhysicalModel(
borderRadius: new BorderRadius.circular(20.0),
color: Colors.red,
clipBehavior: clipBehavior,
child: new Container(
color: Colors.white,
child: new RepaintBoundary(
child: new Center(
child: new Container(
color: Colors.black,
height: 10.0,
width: 10.0,
),
),
),
),
),
),
),
),
),
),
);
}
testWidgets('PhysicalModel painting with Clip.antiAlias', (WidgetTester tester) async {
await tester.pumpWidget(genPhysicalModel(Clip.antiAlias));
await expectLater(
find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalModel.antiAlias.png'),
);
});
testWidgets('PhysicalModel painting with Clip.hardEdge', (WidgetTester tester) async {
await tester.pumpWidget(genPhysicalModel(Clip.hardEdge));
await expectLater(
find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalModel.hardEdge.png'),
);
});
// There will be bleeding edges on the rect edges, but there shouldn't be any bleeding edges on the
// round corners.
testWidgets('PhysicalModel painting with Clip.antiAliasWithSaveLayer', (WidgetTester tester) async {
await tester.pumpWidget(genPhysicalModel(Clip.antiAliasWithSaveLayer));
await expectLater(
find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalModel.antiAliasWithSaveLayer.png'),
);
});
testWidgets('Default PhysicalModel painting', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
new Center( new Center(
child: new RepaintBoundary( child: new RepaintBoundary(
...@@ -469,7 +532,72 @@ void main() { ...@@ -469,7 +532,72 @@ void main() {
); );
await expectLater( await expectLater(
find.byType(RepaintBoundary).first, find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalModel.1.png'), matchesGoldenFile('clip.PhysicalModel.default.png'),
);
});
Center genPhysicalShape(Clip clipBehavior) {
return new Center(
child: new RepaintBoundary(
child: new Container(
color: Colors.white,
child: new Padding(
padding: const EdgeInsets.all(100.0),
child: new SizedBox(
height: 100.0,
width: 100.0,
child: new Transform.rotate(
angle: 1.0, // radians
child: new PhysicalShape(
clipper: new ShapeBorderClipper(
shape: new BeveledRectangleBorder(
borderRadius: new BorderRadius.circular(20.0),
),
),
clipBehavior: clipBehavior,
color: Colors.red,
child: new Container(
color: Colors.white,
child: new RepaintBoundary(
child: new Center(
child: new Container(
color: Colors.black,
height: 10.0,
width: 10.0,
),
),
),
),
),
),
),
),
),
),
);
}
testWidgets('PhysicalShape painting with Clip.antiAlias', (WidgetTester tester) async {
await tester.pumpWidget(genPhysicalShape(Clip.antiAlias));
await expectLater(
find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalShape.antiAlias.png'),
);
});
testWidgets('PhysicalShape painting with Clip.hardEdge', (WidgetTester tester) async {
await tester.pumpWidget(genPhysicalShape(Clip.hardEdge));
await expectLater(
find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalShape.hardEdge.png'),
);
});
testWidgets('PhysicalShape painting with Clip.antiAliasWithSaveLayer', (WidgetTester tester) async {
await tester.pumpWidget(genPhysicalShape(Clip.antiAliasWithSaveLayer));
await expectLater(
find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalShape.antiAliasWithSaveLayer.png'),
); );
}); });
...@@ -515,7 +643,7 @@ void main() { ...@@ -515,7 +643,7 @@ void main() {
); );
await expectLater( await expectLater(
find.byType(RepaintBoundary).first, find.byType(RepaintBoundary).first,
matchesGoldenFile('clip.PhysicalShape.1.png'), matchesGoldenFile('clip.PhysicalShape.default.png'),
); );
}); });
} }
...@@ -1046,6 +1046,11 @@ class _IsMethodCall extends Matcher { ...@@ -1046,6 +1046,11 @@ class _IsMethodCall extends Matcher {
/// [RenderClipPath]. /// [RenderClipPath].
const Matcher clipsWithBoundingRect = _ClipsWithBoundingRect(); const Matcher clipsWithBoundingRect = _ClipsWithBoundingRect();
/// Asserts that a [Finder] locates a single object whose root RenderObject is
/// not a [RenderClipRect], [RenderClipRRect], [RenderClipOval], or
/// [RenderClipPath].
const Matcher hasNoImmediateClip = _MatchAnythingExceptClip();
/// Asserts that a [Finder] locates a single object whose root RenderObject /// Asserts that a [Finder] locates a single object whose root RenderObject
/// is a [RenderClipRRect] with no clipper set, and border radius equals to /// is a [RenderClipRRect] with no clipper set, and border radius equals to
/// [borderRadius], or an equivalent [RenderClipPath]. /// [borderRadius], or an equivalent [RenderClipPath].
...@@ -1104,7 +1109,53 @@ Matcher rendersOnPhysicalShape({ ...@@ -1104,7 +1109,53 @@ Matcher rendersOnPhysicalShape({
); );
} }
abstract class _MatchRenderObject<M extends RenderObject, T extends RenderObject> extends Matcher { abstract class _FailWithDescriptionMatcher extends Matcher {
const _FailWithDescriptionMatcher();
bool failWithDescription(Map<dynamic, dynamic> matchState, String description) {
matchState['failure'] = description;
return false;
}
@override
Description describeMismatch(
dynamic item,
Description mismatchDescription,
Map<dynamic, dynamic> matchState,
bool verbose
) {
return mismatchDescription.add(matchState['failure']);
}
}
class _MatchAnythingExceptClip extends _FailWithDescriptionMatcher {
const _MatchAnythingExceptClip();
@override
bool matches(covariant Finder finder, Map<dynamic, dynamic> matchState) {
final Iterable<Element> nodes = finder.evaluate();
if (nodes.length != 1)
return failWithDescription(matchState, 'did not have a exactly one child element');
final RenderObject renderObject = nodes.single.renderObject;
switch (renderObject.runtimeType) {
case RenderClipPath:
case RenderClipOval:
case RenderClipRect:
case RenderClipRRect:
return failWithDescription(matchState, 'had a root render object of type: ${renderObject.runtimeType}');
default:
return true;
}
}
@override
Description describe(Description description) {
description.add('does not have a clip as an immediate child');
}
}
abstract class _MatchRenderObject<M extends RenderObject, T extends RenderObject> extends _FailWithDescriptionMatcher {
const _MatchRenderObject(); const _MatchRenderObject();
bool renderObjectMatchesT(Map<dynamic, dynamic> matchState, T renderObject); bool renderObjectMatchesT(Map<dynamic, dynamic> matchState, T renderObject);
...@@ -1125,21 +1176,6 @@ abstract class _MatchRenderObject<M extends RenderObject, T extends RenderObject ...@@ -1125,21 +1176,6 @@ abstract class _MatchRenderObject<M extends RenderObject, T extends RenderObject
return failWithDescription(matchState, 'had a root render object of type: ${renderObject.runtimeType}'); return failWithDescription(matchState, 'had a root render object of type: ${renderObject.runtimeType}');
} }
bool failWithDescription(Map<dynamic, dynamic> matchState, String description) {
matchState['failure'] = description;
return false;
}
@override
Description describeMismatch(
dynamic item,
Description mismatchDescription,
Map<dynamic, dynamic> matchState,
bool verbose
) {
return mismatchDescription.add(matchState['failure']);
}
} }
class _RendersOnPhysicalModel extends _MatchRenderObject<RenderPhysicalShape, RenderPhysicalModel> { class _RendersOnPhysicalModel extends _MatchRenderObject<RenderPhysicalShape, RenderPhysicalModel> {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment