Skip to content

Commit fc19ecf

Browse files
authored
Fix SegmentedButton clipping when drawing segments (#149739)
fixes [`SegmentedButton` doesn't clip properly when one of the segments has `ColorFiltered`](flutter/flutter#144990) ### Code sample <details> <summary>expand to view the code sample</summary> ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @OverRide Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: SegmentedButton<int>( segments: const <ButtonSegment<int>>[ ButtonSegment<int>( value: 0, label: ColorFiltered( colorFilter: ColorFilter.mode(Colors.amber, BlendMode.colorBurn), child: Text('Option 1'), ), ), ButtonSegment<int>( value: 1, label: Text('Option 2'), ), ], onSelectionChanged: (Set<int> selected) {}, selected: const <int>{0}, ), ), ), ); } } ``` </details> ### Before ![before](https://github.com/flutter/flutter/assets/48603081/b402fc51-d575-4208-8a71-f798ef2b2bf5) ### After ![after](https://github.com/flutter/flutter/assets/48603081/77a5cac2-ecef-4381-a043-dbb5c91407ec)
1 parent bb831b7 commit fc19ecf

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

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

+3-5
Original file line numberDiff line numberDiff line change
@@ -895,12 +895,11 @@ class _RenderSegmentedButton<T> extends RenderBox with
895895
Path? enabledClipPath;
896896
Path? disabledClipPath;
897897

898-
context.canvas..save()..clipPath(borderClipPath);
899898
while (child != null) {
900899
final _SegmentedButtonContainerBoxParentData childParentData = child.parentData! as _SegmentedButtonContainerBoxParentData;
901900
final Rect childRect = childParentData.surroundingRect!.outerRect.shift(offset);
902901

903-
context.canvas..save()..clipRect(childRect);
902+
context.canvas..save()..clipPath(borderClipPath);
904903
context.paintChild(child, childParentData.offset + offset);
905904
context.canvas.restore();
906905

@@ -935,16 +934,15 @@ class _RenderSegmentedButton<T> extends RenderBox with
935934
final BorderSide divider = segments[index - 1].enabled || segments[index].enabled
936935
? enabledBorder.side.copyWith(strokeAlign: 0.0)
937936
: disabledBorder.side.copyWith(strokeAlign: 0.0);
938-
final Offset top = Offset(dividerPos, childRect.top);
939-
final Offset bottom = Offset(dividerPos, childRect.bottom);
937+
final Offset top = Offset(dividerPos, borderRect.top);
938+
final Offset bottom = Offset(dividerPos, borderRect.bottom);
940939
context.canvas.drawLine(top, bottom, divider.toPaint());
941940
}
942941

943942
previousChild = child;
944943
child = childAfter(child);
945944
index += 1;
946945
}
947-
context.canvas.restore();
948946

949947
// Paint the outer border for both disabled and enabled clip rect if needed.
950948
if (disabledClipPath == null) {

packages/flutter/test/material/segmented_button_test.dart

+73
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,79 @@ void main() {
10451045
await tester.pumpAndSettle();
10461046
expect(getOverlayColor(tester), paints..rect(color: overlayColor));
10471047
});
1048+
1049+
// This is a regression test for https://github.com/flutter/flutter/issues/144990.
1050+
testWidgets('SegmentedButton clips border path when drawing segments', (WidgetTester tester) async {
1051+
await tester.pumpWidget(
1052+
MaterialApp(
1053+
home: Scaffold(
1054+
body: Center(
1055+
child: SegmentedButton<int>(
1056+
segments: const <ButtonSegment<int>>[
1057+
ButtonSegment<int>(
1058+
value: 0,
1059+
label: Text('Option 1'),
1060+
),
1061+
ButtonSegment<int>(
1062+
value: 1,
1063+
label: Text('Option 2'),
1064+
),
1065+
],
1066+
onSelectionChanged: (Set<int> selected) {},
1067+
selected: const <int>{0},
1068+
),
1069+
),
1070+
),
1071+
),
1072+
);
1073+
1074+
expect(
1075+
find.byType(SegmentedButton<int>),
1076+
paints
1077+
..save()
1078+
..clipPath() // Clip the border.
1079+
..path(color: const Color(0xffe8def8)) // Draw segment 0.
1080+
..save()
1081+
..clipPath() // Clip the border.
1082+
..path(color: const Color(0x00000000)), // Draw segment 1.
1083+
);
1084+
});
1085+
1086+
// This is a regression test for https://github.com/flutter/flutter/issues/144990.
1087+
testWidgets('SegmentedButton dividers matches border rect size', (WidgetTester tester) async {
1088+
await tester.pumpWidget(
1089+
MaterialApp(
1090+
home: Scaffold(
1091+
body: Center(
1092+
child: SegmentedButton<int>(
1093+
segments: const <ButtonSegment<int>>[
1094+
ButtonSegment<int>(
1095+
value: 0,
1096+
label: Text('Option 1'),
1097+
),
1098+
ButtonSegment<int>(
1099+
value: 1,
1100+
label: Text('Option 2'),
1101+
),
1102+
],
1103+
onSelectionChanged: (Set<int> selected) {},
1104+
selected: const <int>{0},
1105+
),
1106+
),
1107+
),
1108+
),
1109+
);
1110+
1111+
const double tapTargetSize = 48.0;
1112+
expect(
1113+
find.byType(SegmentedButton<int>),
1114+
paints
1115+
..line(
1116+
p1: const Offset(166.8000030517578, 4.0),
1117+
p2: const Offset(166.8000030517578, tapTargetSize - 4.0),
1118+
),
1119+
);
1120+
});
10481121
}
10491122

10501123
Set<MaterialState> enabled = const <MaterialState>{};

0 commit comments

Comments
 (0)