Skip to content

Commit 9ffa1c5

Browse files
authored
Add Clip enum to Material and related widgets (flutter#18576)
See details in our proposal for this breaking API change and flutter#18057. This PR setup all code paths to allow the change but doesn't change the clip behavior by itself. We'll change `defaultClipBehavior` from `Clip.antiAlias` to `Clip.none` in the following PR to change the clip behavior and update tests.
1 parent ad16374 commit 9ffa1c5

File tree

9 files changed

+237
-82
lines changed

9 files changed

+237
-82
lines changed

packages/flutter/lib/painting.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export 'src/painting/box_decoration.dart';
2828
export 'src/painting/box_fit.dart';
2929
export 'src/painting/box_shadow.dart';
3030
export 'src/painting/circle_border.dart';
31+
export 'src/painting/clip.dart';
3132
export 'src/painting/colors.dart';
3233
export 'src/painting/debug.dart';
3334
export 'src/painting/decoration.dart';

packages/flutter/lib/src/material/material.dart

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:ui' show Clip, defaultClipBehavior; // ignore: deprecated_member_use
6+
57
import 'package:flutter/foundation.dart';
68
import 'package:flutter/rendering.dart';
79
import 'package:flutter/widgets.dart';
@@ -87,8 +89,9 @@ abstract class MaterialInkController {
8789
///
8890
/// The Material widget is responsible for:
8991
///
90-
/// 1. Clipping: Material clips its widget sub-tree to the shape specified by
91-
/// [shape], [type], and [borderRadius].
92+
/// 1. Clipping: If [clipBehavior] is not [Clip.none], Material clips its widget
93+
/// sub-tree to the shape specified by [shape], [type], and [borderRadius].
94+
/// By default, [clipBehavior] is [Clip.none] for performance considerations.
9295
/// 2. Elevation: Material elevates its widget sub-tree on the Z axis by
9396
/// [elevation] pixels, and draws the appropriate shadow.
9497
/// 3. Ink effects: Material shows ink effects implemented by [InkFeature]s
@@ -171,6 +174,7 @@ class Material extends StatefulWidget {
171174
this.textStyle,
172175
this.borderRadius,
173176
this.shape,
177+
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
174178
this.animationDuration = kThemeChangeDuration,
175179
this.child,
176180
}) : assert(type != null),
@@ -179,6 +183,7 @@ class Material extends StatefulWidget {
179183
assert(!(shape != null && borderRadius != null)),
180184
assert(animationDuration != null),
181185
assert(!(identical(type, MaterialType.circle) && (borderRadius != null || shape != null))),
186+
assert(clipBehavior != null),
182187
super(key: key);
183188

184189
/// The widget below this widget in the tree.
@@ -226,6 +231,14 @@ class Material extends StatefulWidget {
226231
/// zero.
227232
final ShapeBorder shape;
228233

234+
/// {@template flutter.widgets.Clip}
235+
/// The content will be clipped (or not) according to this option.
236+
///
237+
/// See the enum [Clip] for details of all possible options and their common
238+
/// use cases.
239+
/// {@endtemplate}
240+
final Clip clipBehavior;
241+
229242
/// Defines the duration of animated changes for [shape], [elevation],
230243
/// and [shadowColor].
231244
///
@@ -329,6 +342,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
329342
curve: Curves.fastOutSlowIn,
330343
duration: widget.animationDuration,
331344
shape: BoxShape.rectangle,
345+
clipBehavior: widget.clipBehavior,
332346
borderRadius: BorderRadius.zero,
333347
elevation: widget.elevation,
334348
color: backgroundColor,
@@ -341,20 +355,21 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
341355
final ShapeBorder shape = _getShape();
342356

343357
if (widget.type == MaterialType.transparency)
344-
return _transparentInterior(shape: shape, contents: contents);
358+
return _transparentInterior(shape: shape, clipBehavior: widget.clipBehavior, contents: contents);
345359

346360
return new _MaterialInterior(
347361
curve: Curves.fastOutSlowIn,
348362
duration: widget.animationDuration,
349363
shape: shape,
364+
clipBehavior: widget.clipBehavior,
350365
elevation: widget.elevation,
351366
color: backgroundColor,
352367
shadowColor: widget.shadowColor,
353368
child: contents,
354369
);
355370
}
356371

357-
static Widget _transparentInterior({ShapeBorder shape, Widget contents}) {
372+
static Widget _transparentInterior({ShapeBorder shape, Clip clipBehavior, Widget contents}) {
358373
return new ClipPath(
359374
child: new _ShapeBorderPaint(
360375
child: contents,
@@ -363,6 +378,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
363378
clipper: new ShapeBorderClipper(
364379
shape: shape,
365380
),
381+
clipBehavior: clipBehavior,
366382
);
367383
}
368384

@@ -582,13 +598,15 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
582598
Key key,
583599
@required this.child,
584600
@required this.shape,
601+
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
585602
@required this.elevation,
586603
@required this.color,
587604
@required this.shadowColor,
588605
Curve curve = Curves.linear,
589606
@required Duration duration,
590607
}) : assert(child != null),
591608
assert(shape != null),
609+
assert(clipBehavior != null),
592610
assert(elevation != null),
593611
assert(color != null),
594612
assert(shadowColor != null),
@@ -605,6 +623,9 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
605623
/// determines the physical shape.
606624
final ShapeBorder shape;
607625

626+
/// {@macro flutter.widgets.Clip}
627+
final Clip clipBehavior;
628+
608629
/// The target z-coordinate at which to place this physical object.
609630
final double elevation;
610631

@@ -651,6 +672,7 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
651672
shape: shape,
652673
textDirection: Directionality.of(context)
653674
),
675+
clipBehavior: widget.clipBehavior,
654676
elevation: _elevation.evaluate(animation),
655677
color: widget.color,
656678
shadowColor: _shadowColor.evaluate(animation),
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:ui' show Canvas, Clip, Path, Paint, Rect, RRect;
6+
7+
/// Clip utilities used by [PaintingContext] and [TestRecordingPaintingContext].
8+
abstract class ClipContext {
9+
/// The canvas on which to paint.
10+
Canvas get canvas;
11+
12+
void _clipAndPaint(void canvasClipCall(bool doAntiAlias), Clip clipBehavior, Rect bounds, void painter()) {
13+
assert(canvasClipCall != null);
14+
canvas.save();
15+
switch (clipBehavior) {
16+
case Clip.none:
17+
break;
18+
case Clip.hardEdge:
19+
canvasClipCall(false);
20+
break;
21+
case Clip.antiAlias:
22+
canvasClipCall(true);
23+
break;
24+
case Clip.antiAliasWithSaveLayer:
25+
canvasClipCall(true);
26+
canvas.saveLayer(bounds, new Paint());
27+
break;
28+
}
29+
painter();
30+
if (clipBehavior == Clip.antiAliasWithSaveLayer) {
31+
canvas.restore();
32+
}
33+
canvas.restore();
34+
}
35+
36+
/// Clip [canvas] with [Path] according to [Clip] and then paint. [canvas] is
37+
/// restored to the pre-clip status afterwards.
38+
///
39+
/// `bounds` is the saveLayer bounds used for [Clip.antiAliasWithSaveLayer].
40+
void clipPathAndPaint(Path path, Clip clipBehavior, Rect bounds, void painter()) {
41+
_clipAndPaint((bool doAntiAias) => canvas.clipPath(path, doAntiAlias: doAntiAias), clipBehavior, bounds, painter);
42+
}
43+
44+
/// Clip [canvas] with [Path] according to [RRect] and then paint. [canvas] is
45+
/// restored to the pre-clip status afterwards.
46+
///
47+
/// `bounds` is the saveLayer bounds used for [Clip.antiAliasWithSaveLayer].
48+
void clipRRectAndPaint(RRect rrect, Clip clipBehavior, Rect bounds, void painter()) {
49+
_clipAndPaint((bool doAntiAias) => canvas.clipRRect(rrect, doAntiAlias: doAntiAias), clipBehavior, bounds, painter);
50+
}
51+
52+
/// Clip [canvas] with [Path] according to [Rect] and then paint. [canvas] is
53+
/// restored to the pre-clip status afterwards.
54+
///
55+
/// `bounds` is the saveLayer bounds used for [Clip.antiAliasWithSaveLayer].
56+
void clipRectAndPaint(Rect rect, Clip clipBehavior, Rect bounds, void painter()) {
57+
_clipAndPaint((bool doAntiAias) => canvas.clipRect(rect, doAntiAlias: doAntiAias), clipBehavior, bounds, painter);
58+
}
59+
}

packages/flutter/lib/src/rendering/layer.dart

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import 'dart:async';
66
import 'dart:collection';
77
import 'dart:ui' as ui show Image, ImageFilter, Picture, Scene, SceneBuilder;
8-
import 'dart:ui' show Offset;
8+
import 'dart:ui' show Clip, Offset, defaultClipBehavior; // ignore: deprecated_member_use
99

1010
import 'package:flutter/foundation.dart';
1111
import 'package:flutter/painting.dart';
@@ -605,14 +605,24 @@ class ClipRectLayer extends ContainerLayer {
605605
///
606606
/// The [clipRect] property must be non-null before the compositing phase of
607607
/// the pipeline.
608-
ClipRectLayer({ this.clipRect });
608+
ClipRectLayer({ this.clipRect, Clip clipBehavior = Clip.antiAlias }) :
609+
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none);
609610

610611
/// The rectangle to clip in the parent's coordinate system.
611612
///
612613
/// The scene must be explicitly recomposited after this property is changed
613614
/// (as described at [Layer]).
614615
Rect clipRect;
615616

617+
/// {@macro flutter.clipper.clipBehavior}
618+
Clip get clipBehavior => _clipBehavior;
619+
Clip _clipBehavior;
620+
set clipBehavior(Clip value) {
621+
assert(value != null);
622+
assert(value != Clip.none);
623+
_clipBehavior = value;
624+
}
625+
616626
@override
617627
S find<S>(Offset regionOffset) {
618628
if (!clipRect.contains(regionOffset))
@@ -628,7 +638,7 @@ class ClipRectLayer extends ContainerLayer {
628638
return true;
629639
}());
630640
if (enabled)
631-
builder.pushClipRect(clipRect.shift(layerOffset));
641+
builder.pushClipRect(clipRect.shift(layerOffset), clipBehavior: clipBehavior);
632642
addChildrenToScene(builder, layerOffset);
633643
if (enabled)
634644
builder.pop();
@@ -651,14 +661,24 @@ class ClipRRectLayer extends ContainerLayer {
651661
///
652662
/// The [clipRRect] property must be non-null before the compositing phase of
653663
/// the pipeline.
654-
ClipRRectLayer({ this.clipRRect });
664+
ClipRRectLayer({ this.clipRRect, Clip clipBehavior = Clip.antiAlias }) :
665+
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none);
655666

656667
/// The rounded-rect to clip in the parent's coordinate system.
657668
///
658669
/// The scene must be explicitly recomposited after this property is changed
659670
/// (as described at [Layer]).
660671
RRect clipRRect;
661672

673+
/// {@macro flutter.clipper.clipBehavior}
674+
Clip get clipBehavior => _clipBehavior;
675+
Clip _clipBehavior;
676+
set clipBehavior(Clip value) {
677+
assert(value != null);
678+
assert(value != Clip.none);
679+
_clipBehavior = value;
680+
}
681+
662682
@override
663683
S find<S>(Offset regionOffset) {
664684
if (!clipRRect.contains(regionOffset))
@@ -674,7 +694,7 @@ class ClipRRectLayer extends ContainerLayer {
674694
return true;
675695
}());
676696
if (enabled)
677-
builder.pushClipRRect(clipRRect.shift(layerOffset));
697+
builder.pushClipRRect(clipRRect.shift(layerOffset), clipBehavior: clipBehavior);
678698
addChildrenToScene(builder, layerOffset);
679699
if (enabled)
680700
builder.pop();
@@ -697,14 +717,24 @@ class ClipPathLayer extends ContainerLayer {
697717
///
698718
/// The [clipPath] property must be non-null before the compositing phase of
699719
/// the pipeline.
700-
ClipPathLayer({ this.clipPath });
720+
ClipPathLayer({ this.clipPath, Clip clipBehavior = Clip.antiAlias }) :
721+
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none);
701722

702723
/// The path to clip in the parent's coordinate system.
703724
///
704725
/// The scene must be explicitly recomposited after this property is changed
705726
/// (as described at [Layer]).
706727
Path clipPath;
707728

729+
/// {@macro flutter.clipper.clipBehavior}
730+
Clip get clipBehavior => _clipBehavior;
731+
Clip _clipBehavior;
732+
set clipBehavior(Clip value) {
733+
assert(value != null);
734+
assert(value != Clip.none);
735+
_clipBehavior = value;
736+
}
737+
708738
@override
709739
S find<S>(Offset regionOffset) {
710740
if (!clipPath.contains(regionOffset))
@@ -720,7 +750,7 @@ class ClipPathLayer extends ContainerLayer {
720750
return true;
721751
}());
722752
if (enabled)
723-
builder.pushClipPath(clipPath.shift(layerOffset));
753+
builder.pushClipPath(clipPath.shift(layerOffset), clipBehavior: clipBehavior);
724754
addChildrenToScene(builder, layerOffset);
725755
if (enabled)
726756
builder.pop();
@@ -925,10 +955,12 @@ class PhysicalModelLayer extends ContainerLayer {
925955
/// The [clipPath], [elevation], and [color] arguments must not be null.
926956
PhysicalModelLayer({
927957
@required this.clipPath,
958+
this.clipBehavior = defaultClipBehavior, // ignore: deprecated_member_use
928959
@required this.elevation,
929960
@required this.color,
930961
@required this.shadowColor,
931962
}) : assert(clipPath != null),
963+
assert(clipBehavior != null),
932964
assert(elevation != null),
933965
assert(color != null),
934966
assert(shadowColor != null);
@@ -939,6 +971,9 @@ class PhysicalModelLayer extends ContainerLayer {
939971
/// (as described at [Layer]).
940972
Path clipPath;
941973

974+
/// {@macro flutter.widgets.Clip}
975+
Clip clipBehavior;
976+
942977
/// The z-coordinate at which to place this physical object.
943978
///
944979
/// The scene must be explicitly recomposited after this property is changed
@@ -980,6 +1015,7 @@ class PhysicalModelLayer extends ContainerLayer {
9801015
elevation: elevation,
9811016
color: color,
9821017
shadowColor: shadowColor,
1018+
clipBehavior: clipBehavior,
9831019
);
9841020
}
9851021
addChildrenToScene(builder, layerOffset);

0 commit comments

Comments
 (0)