Skip to content

Commit

Permalink
Fix SegmentedButton clipping when drawing segments (flutter#149739)
Browse files Browse the repository at this point in the history
fixes [`SegmentedButton` doesn't clip properly when one of the segments has `ColorFiltered`](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)
  • Loading branch information
TahaTesser authored Jun 7, 2024
1 parent bb831b7 commit fc19ecf
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 5 deletions.
8 changes: 3 additions & 5 deletions packages/flutter/lib/src/material/segmented_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -935,16 +934,15 @@ 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());
}

previousChild = child;
child = childAfter(child);
index += 1;
}
context.canvas.restore();

// Paint the outer border for both disabled and enabled clip rect if needed.
if (disabledClipPath == null) {
Expand Down
73 changes: 73 additions & 0 deletions packages/flutter/test/material/segmented_button_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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>{};
Expand Down

0 comments on commit fc19ecf

Please sign in to comment.