Skip to content

Commit 1662a14

Browse files
authored
More missing clipBehavior respects (#103931)
1 parent a633f3d commit 1662a14

13 files changed

+183
-16
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2526,7 +2526,16 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
25262526
final LayerHandle<ClipRectLayer> _clipRectLayer = LayerHandle<ClipRectLayer>();
25272527

25282528
@override
2529-
Rect? describeApproximatePaintClip(RenderObject child) => _hasVisualOverflow ? Offset.zero & size : null;
2529+
Rect? describeApproximatePaintClip(RenderObject child) {
2530+
switch (clipBehavior) {
2531+
case Clip.none:
2532+
return null;
2533+
case Clip.hardEdge:
2534+
case Clip.antiAlias:
2535+
case Clip.antiAliasWithSaveLayer:
2536+
return _hasVisualOverflow ? Offset.zero & size : null;
2537+
}
2538+
}
25302539

25312540
@override
25322541
void debugFillProperties(DiagnosticPropertiesBuilder properties) {

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1144,7 +1144,17 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
11441144
}
11451145

11461146
@override
1147-
Rect? describeApproximatePaintClip(RenderObject child) => _hasOverflow ? Offset.zero & size : null;
1147+
Rect? describeApproximatePaintClip(RenderObject child) {
1148+
switch (clipBehavior) {
1149+
case Clip.none:
1150+
return null;
1151+
case Clip.hardEdge:
1152+
case Clip.antiAlias:
1153+
case Clip.antiAliasWithSaveLayer:
1154+
return _hasOverflow ? Offset.zero & size : null;
1155+
}
1156+
}
1157+
11481158

11491159
@override
11501160
String toStringShort() {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
27882788
///
27892789
/// This is used in the semantics phase to avoid including children
27902790
/// that are not physically visible.
2791+
///
2792+
/// RenderObjects that respect a [Clip] behavior when painting _must_ respect
2793+
/// that same behavior when describing this value. For example, if passing
2794+
/// [Clip.none] to [PaintingContext.pushClipRect] as the `clipBehavior`, then
2795+
/// the implementation of this method must return null.
27912796
Rect? describeApproximatePaintClip(covariant RenderObject child) => null;
27922797

27932798
/// Returns a rect in this object's coordinate system that describes

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,14 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO
826826

827827
@override
828828
Rect? describeApproximatePaintClip(RenderObject child) {
829-
return _isOverflowing ? Offset.zero & size : null;
829+
switch (clipBehavior) {
830+
case Clip.none:
831+
return null;
832+
case Clip.hardEdge:
833+
case Clip.antiAlias:
834+
case Clip.antiAliasWithSaveLayer:
835+
return _isOverflowing ? Offset.zero & size : null;
836+
}
830837
}
831838

832839
@override

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,16 @@ class RenderStack extends RenderBox
636636
}
637637

638638
@override
639-
Rect? describeApproximatePaintClip(RenderObject child) => _hasVisualOverflow ? Offset.zero & size : null;
639+
Rect? describeApproximatePaintClip(RenderObject child) {
640+
switch (clipBehavior) {
641+
case Clip.none:
642+
return null;
643+
case Clip.hardEdge:
644+
case Clip.antiAlias:
645+
case Clip.antiAliasWithSaveLayer:
646+
return _hasVisualOverflow ? Offset.zero & size : null;
647+
}
648+
}
640649

641650
@override
642651
void debugFillProperties(DiagnosticPropertiesBuilder properties) {

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,16 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
562562
}
563563

564564
@override
565-
Rect describeApproximatePaintClip(RenderSliver child) {
565+
Rect? describeApproximatePaintClip(RenderSliver child) {
566+
switch (clipBehavior) {
567+
case Clip.none:
568+
return null;
569+
case Clip.hardEdge:
570+
case Clip.antiAlias:
571+
case Clip.antiAliasWithSaveLayer:
572+
break;
573+
}
574+
566575
final Rect viewportClip = Offset.zero & size;
567576
// The child's viewportMainAxisExtent can be infinite when a
568577
// RenderShrinkWrappingViewport is given infinite constraints, such as when

packages/flutter/lib/src/widgets/overlay.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,16 @@ class _RenderTheatre extends RenderBox with ContainerRenderObjectMixin<RenderBox
866866
}
867867

868868
@override
869-
Rect? describeApproximatePaintClip(RenderObject child) => _hasVisualOverflow ? Offset.zero & size : null;
869+
Rect? describeApproximatePaintClip(RenderObject child) {
870+
switch (clipBehavior) {
871+
case Clip.none:
872+
return null;
873+
case Clip.hardEdge:
874+
case Clip.antiAlias:
875+
case Clip.antiAliasWithSaveLayer:
876+
return _hasVisualOverflow ? Offset.zero & size : null;
877+
}
878+
}
870879

871880
@override
872881
void debugFillProperties(DiagnosticPropertiesBuilder properties) {

packages/flutter/lib/src/widgets/single_child_scroll_view.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,10 +533,17 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
533533

534534
bool _shouldClipAtPaintOffset(Offset paintOffset) {
535535
assert(child != null);
536-
return paintOffset.dx < 0 ||
537-
paintOffset.dy < 0 ||
538-
paintOffset.dx + child!.size.width > size.width ||
539-
paintOffset.dy + child!.size.height > size.height;
536+
switch (clipBehavior) {
537+
case Clip.none:
538+
return false;
539+
case Clip.hardEdge:
540+
case Clip.antiAlias:
541+
case Clip.antiAliasWithSaveLayer:
542+
return paintOffset.dx < 0 ||
543+
paintOffset.dy < 0 ||
544+
paintOffset.dx + child!.size.width > size.width ||
545+
paintOffset.dy + child!.size.height > size.height;
546+
}
540547
}
541548

542549
@override
@@ -548,7 +555,7 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
548555
context.paintChild(child!, offset + paintOffset);
549556
}
550557

551-
if (_shouldClipAtPaintOffset(paintOffset) && clipBehavior != Clip.none) {
558+
if (_shouldClipAtPaintOffset(paintOffset)) {
552559
_clipRectLayer.layer = context.pushClipRect(
553560
needsCompositing,
554561
offset,

packages/flutter/test/rendering/editable_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,28 @@ void main() {
128128
}
129129
});
130130

131+
test('Editable respect clipBehavior in describeApproximatePaintClip', () {
132+
final String longString = 'a' * 10000;
133+
final RenderEditable editable = RenderEditable(
134+
text: TextSpan(text: longString),
135+
textDirection: TextDirection.ltr,
136+
startHandleLayerLink: LayerLink(),
137+
endHandleLayerLink: LayerLink(),
138+
offset: ViewportOffset.zero(),
139+
textSelectionDelegate: _FakeEditableTextState(),
140+
selection: const TextSelection(baseOffset: 0, extentOffset: 0),
141+
clipBehavior: Clip.none,
142+
);
143+
layout(editable);
144+
145+
bool visited = false;
146+
editable.visitChildren((RenderObject child) {
147+
visited = true;
148+
expect(editable.describeApproximatePaintClip(child), null);
149+
});
150+
expect(visited, true);
151+
});
152+
131153
test('editable intrinsics', () {
132154
final TextSelectionDelegate delegate = _FakeEditableTextState();
133155
final RenderEditable editable = RenderEditable(

packages/flutter/test/rendering/viewport_test.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,4 +2158,41 @@ void main() {
21582158
await tester.drag(find.text('b'), const Offset(0, 200));
21592159
await tester.pumpAndSettle();
21602160
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
2161+
2162+
testWidgets('Viewport describeApproximateClip respects clipBehavior', (WidgetTester tester) async {
2163+
await tester.pumpWidget(const Directionality(
2164+
textDirection: TextDirection.ltr,
2165+
child: CustomScrollView(
2166+
clipBehavior: Clip.none,
2167+
slivers: <Widget>[
2168+
SliverToBoxAdapter(child: SizedBox(width: 20, height: 20)),
2169+
]
2170+
),
2171+
));
2172+
RenderViewport viewport = tester.allRenderObjects.whereType<RenderViewport>().first;
2173+
expect(viewport.clipBehavior, Clip.none);
2174+
bool visited = false;
2175+
viewport.visitChildren((RenderObject child) {
2176+
visited = true;
2177+
expect(viewport.describeApproximatePaintClip(child as RenderSliver), null);
2178+
});
2179+
expect(visited, true);
2180+
2181+
await tester.pumpWidget(const Directionality(
2182+
textDirection: TextDirection.ltr,
2183+
child: CustomScrollView(
2184+
slivers: <Widget>[
2185+
SliverToBoxAdapter(child: SizedBox(width: 20, height: 20)),
2186+
]
2187+
),
2188+
));
2189+
viewport = tester.allRenderObjects.whereType<RenderViewport>().first;
2190+
expect(viewport.clipBehavior, Clip.hardEdge);
2191+
visited = false;
2192+
viewport.visitChildren((RenderObject child) {
2193+
visited = true;
2194+
expect(viewport.describeApproximatePaintClip(child as RenderSliver), Offset.zero & viewport.size);
2195+
});
2196+
expect(visited, true);
2197+
});
21612198
}

packages/flutter/test/widgets/overlay_test.dart

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,24 +1020,23 @@ void main() {
10201020
});
10211021

10221022
testWidgets('Overlay can set and update clipBehavior', (WidgetTester tester) async {
1023-
10241023
await tester.pumpWidget(
10251024
Directionality(
10261025
textDirection: TextDirection.ltr,
10271026
child: Overlay(
10281027
initialEntries: <OverlayEntry>[
10291028
OverlayEntry(
1030-
builder: (BuildContext context) => Container(),
1029+
builder: (BuildContext context) => Positioned(left: 2000, right: 2500, child: Container()),
10311030
),
10321031
],
10331032
),
10341033
),
10351034
);
10361035

10371036
// By default, clipBehavior should be Clip.hardEdge
1038-
final dynamic renderObject = tester.renderObject(find.byType(Overlay));
1037+
final RenderObject renderObject = tester.renderObject(find.byType(Overlay));
10391038
// ignore: avoid_dynamic_calls
1040-
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
1039+
expect((renderObject as dynamic).clipBehavior, equals(Clip.hardEdge));
10411040

10421041
for (final Clip clip in Clip.values) {
10431042
await tester.pumpWidget(
@@ -1053,8 +1052,27 @@ void main() {
10531052
),
10541053
),
10551054
);
1055+
10561056
// ignore: avoid_dynamic_calls
1057-
expect(renderObject.clipBehavior, clip);
1057+
expect((renderObject as dynamic).clipBehavior, clip);
1058+
bool visited = false;
1059+
renderObject.visitChildren((RenderObject child) {
1060+
visited = true;
1061+
switch(clip) {
1062+
case Clip.none:
1063+
expect(renderObject.describeApproximatePaintClip(child), null);
1064+
break;
1065+
case Clip.hardEdge:
1066+
case Clip.antiAlias:
1067+
case Clip.antiAliasWithSaveLayer:
1068+
expect(
1069+
renderObject.describeApproximatePaintClip(child),
1070+
const Rect.fromLTRB(0, 0, 800, 600),
1071+
);
1072+
break;
1073+
}
1074+
});
1075+
expect(visited, true);
10581076
}
10591077
});
10601078

packages/flutter/test/widgets/semantics_clipping_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ void main() {
1818
child: SizedBox(
1919
width: 100.0,
2020
child: Flex(
21+
clipBehavior: Clip.hardEdge,
2122
direction: Axis.horizontal,
2223
children: const <Widget>[
2324
SizedBox(
@@ -75,6 +76,7 @@ void main() {
7576
child: SizedBox(
7677
width: 100.0,
7778
child: Flex(
79+
clipBehavior: Clip.hardEdge,
7880
direction: Axis.horizontal,
7981
children: <Widget>[
8082
const SizedBox(

packages/flutter/test/widgets/stack_test.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,29 @@ void main() {
385385
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
386386
});
387387

388+
testWidgets('Clip.none is respected by describeApproximateClip', (WidgetTester tester) async {
389+
await tester.pumpWidget(Stack(
390+
textDirection: TextDirection.ltr,
391+
children: const <Widget>[Positioned(left: 1000, right: 2000, child: SizedBox(width: 2000, height: 2000))],
392+
));
393+
final RenderStack renderObject = tester.allRenderObjects.whereType<RenderStack>().first;
394+
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
395+
396+
bool visited = false;
397+
renderObject.visitChildren((RenderObject child) {
398+
visited = true;
399+
expect(renderObject.describeApproximatePaintClip(child), const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0));
400+
});
401+
expect(visited, true);
402+
visited = false;
403+
renderObject.clipBehavior = Clip.none;
404+
renderObject.visitChildren((RenderObject child) {
405+
visited = true;
406+
expect(renderObject.describeApproximatePaintClip(child), null);
407+
});
408+
expect(visited, true);
409+
});
410+
388411
testWidgets('IndexedStack with null index', (WidgetTester tester) async {
389412
bool? tapped;
390413

0 commit comments

Comments
 (0)