From b6e964bd0cdc6fe45540265915b7196ec16cf2bc Mon Sep 17 00:00:00 2001 From: Taha Tesser <48603081+TahaTesser@users.noreply.github.com> Date: Wed, 30 Apr 2025 02:44:25 +0300 Subject: [PATCH] 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
expand to view the code sample ```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: [ Slider( value: 100, max: 100, divisions: 100, onChanged: (double value) {}, ), RangeSlider( values: const RangeValues(0, 100), max: 100, divisions: 100, onChanged: (RangeValues values) {}, ), ], ), ), ); } } ```
### Before Screenshot 2025-03-06 at 13 33 17 ### After Screenshot 2025-03-06 at 13 33 28 ## 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]. [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 --- .../lib/src/material/range_slider.dart | 9 ++-- packages/flutter/lib/src/material/slider.dart | 8 ++-- .../test/material/range_slider_test.dart | 43 +++++++++++++++++++ .../flutter/test/material/slider_test.dart | 42 ++++++++++++++++++ 4 files changed, 93 insertions(+), 9 deletions(-) diff --git a/packages/flutter/lib/src/material/range_slider.dart b/packages/flutter/lib/src/material/range_slider.dart index 5fd0a065f5f..d21cfefb532 100644 --- a/packages/flutter/lib/src/material/range_slider.dart +++ b/packages/flutter/lib/src/material/range_slider.dart @@ -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, diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart index 45e06e5c5da..e42b39bafb6 100644 --- a/packages/flutter/lib/src/material/slider.dart +++ b/packages/flutter/lib/src/material/slider.dart @@ -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, diff --git a/packages/flutter/test/material/range_slider_test.dart b/packages/flutter/test/material/range_slider_test.dart index cc76c0d5e8a..9c9f5a5a64f 100644 --- a/packages/flutter/test/material/range_slider_test.dart +++ b/packages/flutter/test/material/range_slider_test.dart @@ -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), + ); + }); } diff --git a/packages/flutter/test/material/slider_test.dart b/packages/flutter/test/material/slider_test.dart index 5da7f13a218..7b4d4240b20 100644 --- a/packages/flutter/test/material/slider_test.dart +++ b/packages/flutter/test/material/slider_test.dart @@ -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), + ); + }); }