Fix SegmentedButton clipping when drawing segments (#149739)

fixes [`SegmentedButton` doesn't clip properly when one of the segments has `ColorFiltered`](https://github.com/flutter/flutter/issues/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)
This commit is contained in:
Taha Tesser 2024-06-07 21:34:24 +03:00 committed by GitHub
parent bb831b7a64
commit fc19ecfc58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 76 additions and 5 deletions

View File

@ -895,12 +895,11 @@ class _RenderSegmentedButton<T> extends RenderBox with
Path? enabledClipPath;
Path? disabledClipPath;
context.canvas..save()..clipPath(borderClipPath);
while (child != null) {
final _SegmentedButtonContainerBoxParentData childParentData = child.parentData! as _SegmentedButtonContainerBoxParentData;
final Rect childRect = childParentData.surroundingRect!.outerRect.shift(offset);
context.canvas..save()..clipRect(childRect);
context.canvas..save()..clipPath(borderClipPath);
context.paintChild(child, childParentData.offset + offset);
context.canvas.restore();
@ -935,8 +934,8 @@ class _RenderSegmentedButton<T> extends RenderBox with
final BorderSide divider = segments[index - 1].enabled || segments[index].enabled
? enabledBorder.side.copyWith(strokeAlign: 0.0)
: disabledBorder.side.copyWith(strokeAlign: 0.0);
final Offset top = Offset(dividerPos, childRect.top);
final Offset bottom = Offset(dividerPos, childRect.bottom);
final Offset top = Offset(dividerPos, borderRect.top);
final Offset bottom = Offset(dividerPos, borderRect.bottom);
context.canvas.drawLine(top, bottom, divider.toPaint());
}
@ -944,7 +943,6 @@ class _RenderSegmentedButton<T> extends RenderBox with
child = childAfter(child);
index += 1;
}
context.canvas.restore();
// Paint the outer border for both disabled and enabled clip rect if needed.
if (disabledClipPath == null) {

View File

@ -1045,6 +1045,79 @@ void main() {
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor));
});
// This is a regression test for https://github.com/flutter/flutter/issues/144990.
testWidgets('SegmentedButton clips border path when drawing segments', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: SegmentedButton<int>(
segments: const <ButtonSegment<int>>[
ButtonSegment<int>(
value: 0,
label: Text('Option 1'),
),
ButtonSegment<int>(
value: 1,
label: Text('Option 2'),
),
],
onSelectionChanged: (Set<int> selected) {},
selected: const <int>{0},
),
),
),
),
);
expect(
find.byType(SegmentedButton<int>),
paints
..save()
..clipPath() // Clip the border.
..path(color: const Color(0xffe8def8)) // Draw segment 0.
..save()
..clipPath() // Clip the border.
..path(color: const Color(0x00000000)), // Draw segment 1.
);
});
// This is a regression test for https://github.com/flutter/flutter/issues/144990.
testWidgets('SegmentedButton dividers matches border rect size', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: SegmentedButton<int>(
segments: const <ButtonSegment<int>>[
ButtonSegment<int>(
value: 0,
label: Text('Option 1'),
),
ButtonSegment<int>(
value: 1,
label: Text('Option 2'),
),
],
onSelectionChanged: (Set<int> selected) {},
selected: const <int>{0},
),
),
),
),
);
const double tapTargetSize = 48.0;
expect(
find.byType(SegmentedButton<int>),
paints
..line(
p1: const Offset(166.8000030517578, 4.0),
p2: const Offset(166.8000030517578, tapTargetSize - 4.0),
),
);
});
}
Set<MaterialState> enabled = const <MaterialState>{};