Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 074ee4b

Browse files
authored
Allow ClipRRect.borderRadius to support BorderRadiusDirectional (#101200)
1 parent 8ff3640 commit 074ee4b

File tree

3 files changed

+108
-8
lines changed

3 files changed

+108
-8
lines changed

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,11 +1504,13 @@ class RenderClipRRect extends _RenderCustomClip<RRect> {
15041504
/// [Clip.none], no clipping will be applied.
15051505
RenderClipRRect({
15061506
RenderBox? child,
1507-
BorderRadius borderRadius = BorderRadius.zero,
1507+
BorderRadiusGeometry borderRadius = BorderRadius.zero,
15081508
CustomClipper<RRect>? clipper,
15091509
Clip clipBehavior = Clip.antiAlias,
1510+
TextDirection? textDirection,
15101511
}) : assert(clipBehavior != null),
15111512
_borderRadius = borderRadius,
1513+
_textDirection = textDirection,
15121514
super(child: child, clipper: clipper, clipBehavior: clipBehavior) {
15131515
assert(_borderRadius != null || clipper != null);
15141516
}
@@ -1519,18 +1521,28 @@ class RenderClipRRect extends _RenderCustomClip<RRect> {
15191521
/// exceed width/height.
15201522
///
15211523
/// This value is ignored if [clipper] is non-null.
1522-
BorderRadius get borderRadius => _borderRadius;
1523-
BorderRadius _borderRadius;
1524-
set borderRadius(BorderRadius value) {
1524+
BorderRadiusGeometry get borderRadius => _borderRadius;
1525+
BorderRadiusGeometry _borderRadius;
1526+
set borderRadius(BorderRadiusGeometry value) {
15251527
assert(value != null);
15261528
if (_borderRadius == value)
15271529
return;
15281530
_borderRadius = value;
15291531
_markNeedsClip();
15301532
}
15311533

1534+
/// The text direction with which to resolve [borderRadius].
1535+
TextDirection? get textDirection => _textDirection;
1536+
TextDirection? _textDirection;
1537+
set textDirection(TextDirection? value) {
1538+
if (_textDirection == value)
1539+
return;
1540+
_textDirection = value;
1541+
_markNeedsClip();
1542+
}
1543+
15321544
@override
1533-
RRect get _defaultClip => _borderRadius.toRRect(Offset.zero & size);
1545+
RRect get _defaultClip => _borderRadius.resolve(textDirection).toRRect(Offset.zero & size);
15341546

15351547
@override
15361548
bool hitTest(BoxHitTestResult result, { required Offset position }) {

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ class ClipRRect extends SingleChildRenderObjectWidget {
743743
/// exceed width/height.
744744
///
745745
/// This value is ignored if [clipper] is non-null.
746-
final BorderRadius? borderRadius;
746+
final BorderRadiusGeometry? borderRadius;
747747

748748
/// If non-null, determines which clip to use.
749749
final CustomClipper<RRect>? clipper;
@@ -759,6 +759,7 @@ class ClipRRect extends SingleChildRenderObjectWidget {
759759
borderRadius: borderRadius!,
760760
clipper: clipper,
761761
clipBehavior: clipBehavior,
762+
textDirection: Directionality.maybeOf(context),
762763
);
763764
}
764765

@@ -767,13 +768,14 @@ class ClipRRect extends SingleChildRenderObjectWidget {
767768
renderObject
768769
..borderRadius = borderRadius!
769770
..clipBehavior = clipBehavior
770-
..clipper = clipper;
771+
..clipper = clipper
772+
..textDirection = Directionality.maybeOf(context);
771773
}
772774

773775
@override
774776
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
775777
super.debugFillProperties(properties);
776-
properties.add(DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius, showName: false, defaultValue: null));
778+
properties.add(DiagnosticsProperty<BorderRadiusGeometry>('borderRadius', borderRadius, showName: false, defaultValue: null));
777779
properties.add(DiagnosticsProperty<CustomClipper<RRect>>('clipper', clipper, defaultValue: null));
778780
}
779781
}

packages/flutter/test/widgets/clip_test.dart

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,4 +883,90 @@ void main() {
883883
..restore(),
884884
);
885885
});
886+
887+
testWidgets('ClipRRect supports BorderRadiusDirectional', (WidgetTester tester) async {
888+
const Radius startRadius = Radius.circular(15.0);
889+
const Radius endRadius = Radius.circular(30.0);
890+
891+
Widget buildClipRRect(TextDirection textDirection) {
892+
return Directionality(
893+
textDirection: textDirection,
894+
child: const ClipRRect(
895+
borderRadius: BorderRadiusDirectional.horizontal(
896+
start: startRadius,
897+
end: endRadius,
898+
),
899+
),
900+
);
901+
}
902+
903+
for (final TextDirection textDirection in TextDirection.values) {
904+
await tester.pumpWidget(buildClipRRect(textDirection));
905+
final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
906+
final bool isRtl = textDirection == TextDirection.rtl;
907+
expect(renderClip.borderRadius.resolve(textDirection).topLeft, isRtl ? endRadius : startRadius);
908+
expect(renderClip.borderRadius.resolve(textDirection).topRight, isRtl ? startRadius : endRadius);
909+
expect(renderClip.borderRadius.resolve(textDirection).bottomLeft, isRtl ? endRadius : startRadius);
910+
expect(renderClip.borderRadius.resolve(textDirection).bottomRight, isRtl ? startRadius : endRadius);
911+
}
912+
});
913+
914+
testWidgets('ClipRRect is direction-aware', (WidgetTester tester) async {
915+
const Radius startRadius = Radius.circular(15.0);
916+
const Radius endRadius = Radius.circular(30.0);
917+
TextDirection textDirection = TextDirection.ltr;
918+
919+
Widget buildClipRRect(TextDirection textDirection) {
920+
return Directionality(
921+
textDirection: textDirection,
922+
child: const ClipRRect(
923+
borderRadius: BorderRadiusDirectional.horizontal(
924+
start: startRadius,
925+
end: endRadius,
926+
),
927+
),
928+
);
929+
}
930+
931+
Widget buildStatefulClipRRect() {
932+
return StatefulBuilder(
933+
builder: (BuildContext context, StateSetter setState) {
934+
return Directionality(
935+
textDirection: textDirection,
936+
child: SizedBox(
937+
width: 100.0,
938+
height: 100.0,
939+
child: ClipRRect(
940+
borderRadius: const BorderRadiusDirectional.horizontal(
941+
start: startRadius,
942+
end: endRadius,
943+
),
944+
child: GestureDetector(
945+
onTap: () {
946+
setState(() {
947+
textDirection = TextDirection.rtl;
948+
});
949+
},
950+
),
951+
),
952+
),
953+
);
954+
},
955+
);
956+
}
957+
958+
for (final TextDirection textDirection in TextDirection.values) {
959+
await tester.pumpWidget(buildClipRRect(textDirection));
960+
final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
961+
final bool isRtl = textDirection == TextDirection.rtl;
962+
expect(renderClip.textDirection, isRtl ? TextDirection.rtl : TextDirection.ltr);
963+
}
964+
965+
await tester.pumpWidget(buildStatefulClipRRect());
966+
final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
967+
expect(renderClip.textDirection, TextDirection.ltr);
968+
await tester.tapAt(Offset.zero);
969+
await tester.pump();
970+
expect(renderClip.textDirection, TextDirection.rtl);
971+
});
886972
}

0 commit comments

Comments
 (0)