Fix discrete Slider and RangeSlider to enforce thumb height padding when the track shape is non-rounded (#164703)

Fixes [Discrete `Slider` and `RangeSlider` applies thumb padding when
using custom Slider
shapes](https://github.com/flutter/flutter/issues/161805)

### Code Sample

<details>
<summary>expand to view the code sample</summary> 

```dart
import 'package:flutter/material.dart';

void main() => runApp(const RangeSliderExampleApp());

class RangeSliderExampleApp extends StatelessWidget {
  const RangeSliderExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        sliderTheme: const SliderThemeData(
          trackHeight: 32,
          trackShape: RectangularSliderTrackShape(),
          rangeTrackShape: RectangularRangeSliderTrackShape(),
          thumbColor: Colors.amber,
        ),
      ),
      home: Scaffold(
        body: Column(
          spacing: 20.0,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Slider(
              value: 100,
              max: 100,
              divisions: 100,
              onChanged: (double value) {},
            ),
            RangeSlider(
              values: const RangeValues(0, 100),
              max: 100,
              divisions: 100,
              onChanged: (RangeValues values) {},
            ),
          ],
        ),
      ),
    );
  }
}
```

</details>

### Before

<img width="929" alt="Screenshot 2025-03-06 at 13 33 17"
src="https://github.com/user-attachments/assets/a089674f-4931-4808-9663-1cd540bf7b11"
/>

### After

<img width="929" alt="Screenshot 2025-03-06 at 13 33 28"
src="https://github.com/user-attachments/assets/77fe5708-f2e4-4734-92c2-46e4d8338d0e"
/>

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
Taha Tesser 2025-04-30 02:44:25 +03:00 committed by GitHub
parent f88cde1e01
commit b6e964bd0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 93 additions and 9 deletions

View File

@ -1492,8 +1492,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
sliderTheme: _sliderTheme,
isDiscrete: isDiscrete,
);
final double padding =
isDiscrete || _sliderTheme.rangeTrackShape!.isRounded ? trackRect.height : 0.0;
final double padding = _sliderTheme.rangeTrackShape!.isRounded ? trackRect.height : 0.0;
final double thumbYOffset = trackRect.center.dy;
final double startThumbPosition =
isDiscrete
@ -1586,8 +1585,8 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
_sliderTheme.rangeTickMarkShape!
.getPreferredSize(isEnabled: isEnabled, sliderTheme: _sliderTheme)
.width;
final double padding = trackRect.height;
final double adjustedTrackWidth = trackRect.width - padding;
final double discreteTrackPadding = trackRect.height;
final double adjustedTrackWidth = trackRect.width - discreteTrackPadding;
// If the tick marks would be too dense, don't bother painting them.
if (adjustedTrackWidth / divisions! >= 3.0 * tickMarkWidth) {
final double dy = trackRect.center.dy;
@ -1595,7 +1594,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
final double value = i / divisions!;
// The ticks are mapped to be within the track, so the tick mark width
// must be subtracted from the track width.
final double dx = trackRect.left + value * adjustedTrackWidth + padding / 2;
final double dx = trackRect.left + value * adjustedTrackWidth + discreteTrackPadding / 2;
final Offset tickMarkOffset = Offset(dx, dy);
_sliderTheme.rangeTickMarkShape!.paint(
context,

View File

@ -1740,8 +1740,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
sliderTheme: _sliderTheme,
isDiscrete: isDiscrete,
);
final double padding =
isDiscrete || _sliderTheme.trackShape!.isRounded ? trackRect.height : 0.0;
final double padding = _sliderTheme.trackShape!.isRounded ? trackRect.height : 0.0;
final double thumbPosition =
isDiscrete
? trackRect.left + visualPosition * (trackRect.width - padding) + padding / 2
@ -1822,7 +1821,8 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
_sliderTheme.tickMarkShape!
.getPreferredSize(isEnabled: isInteractive, sliderTheme: _sliderTheme)
.width;
final double adjustedTrackWidth = trackRect.width - padding;
final double discreteTrackPadding = trackRect.height;
final double adjustedTrackWidth = trackRect.width - discreteTrackPadding;
// If the tick marks would be too dense, don't bother painting them.
if (adjustedTrackWidth / divisions! >= 3.0 * tickMarkWidth) {
final double dy = trackRect.center.dy;
@ -1830,7 +1830,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
final double value = i / divisions!;
// The ticks are mapped to be within the track, so the tick mark width
// must be subtracted from the track width.
final double dx = trackRect.left + value * adjustedTrackWidth + padding / 2;
final double dx = trackRect.left + value * adjustedTrackWidth + discreteTrackPadding / 2;
final Offset tickMarkOffset = Offset(dx, dy);
_sliderTheme.tickMarkShape!.paint(
context,

View File

@ -3093,4 +3093,47 @@ void main() {
..rrect(rrect: RRect.fromLTRBR(8.0, 7.0, 792.0, 13.0, const Radius.circular(2.0))),
);
});
// Regression test for hhttps://github.com/flutter/flutter/issues/161805
testWidgets('Discrete RangeSlider does not apply thumb padding in a non-rounded track shape', (
WidgetTester tester,
) async {
// The default track left and right padding.
const double sliderPadding = 24.0;
final ThemeData theme = ThemeData(
sliderTheme: const SliderThemeData(
// Thumb padding is applied based on the track height.
trackHeight: 100,
rangeTrackShape: RectangularRangeSliderTrackShape(),
),
);
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: SizedBox(
width: 300,
child: RangeSlider(
values: const RangeValues(0, 100),
max: 100,
divisions: 100,
onChanged: (RangeValues value) {},
),
),
),
),
);
final MaterialInkController material = Material.of(tester.element(find.byType(RangeSlider)));
expect(
material,
paints
// Start thumb.
..circle(x: sliderPadding, y: 300.0, color: theme.colorScheme.primary)
// End thumb.
..circle(x: 800.0 - sliderPadding, y: 300.0, color: theme.colorScheme.primary),
);
});
}

View File

@ -5328,4 +5328,46 @@ void main() {
await tester.pumpAndSettle();
expect(log.last, const Offset(400.0, 300.0));
});
// Regression test for hhttps://github.com/flutter/flutter/issues/161805
testWidgets('Discrete Slider does not apply thumb padding in a non-rounded track shape', (
WidgetTester tester,
) async {
// The default track left and right padding.
const double sliderPadding = 24.0;
final ThemeData theme = ThemeData(
sliderTheme: const SliderThemeData(
// Thumb padding is applied based on the track height.
trackHeight: 100,
trackShape: RectangularSliderTrackShape(),
),
);
Widget buildSlider({required double value}) {
return MaterialApp(
theme: theme,
home: Material(
child: SizedBox(
width: 300,
child: Slider(value: value, max: 100, divisions: 100, onChanged: (double value) {}),
),
),
);
}
await tester.pumpWidget(buildSlider(value: 0));
MaterialInkController material = Material.of(tester.element(find.byType(Slider)));
expect(material, paints..circle(x: sliderPadding, y: 300.0, color: theme.colorScheme.primary));
await tester.pumpWidget(buildSlider(value: 100));
await tester.pumpAndSettle();
material = Material.of(tester.element(find.byType(Slider)));
expect(
material,
paints..circle(x: 800.0 - sliderPadding, y: 300.0, color: theme.colorScheme.primary),
);
});
}