Fix Slider
thumb doesn't align with divisions, thumb padding, and rounded corners (#149594)
fixes [[Slider] Thumb's center doesn't align with division's center](https://github.com/flutter/flutter/issues/62567) fixes [Slider thumb doesn't respect round slider track shape](https://github.com/flutter/flutter/issues/149591) fixes [`RoundedRectSliderTrackShape` corners are not rendered correctly](https://github.com/flutter/flutter/issues/149589) (Verified these behaviors with Android components implementation) ### 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 StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { double _value = 5.0; @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( sliderTheme: const SliderThemeData( trackHeight: 32, thumbColor: Colors.green, activeTrackColor: Colors.deepPurple, inactiveTrackColor: Colors.amber, ), ), home: Scaffold( body: Slider( value: _value, // divisions: 10, // ignore: avoid_redundant_argument_values min: 0, max: 10, onChanged: (double value) { setState(() { _value = value; }); }, ), ), ); } } ``` </details> ### Description This PR fixes several core `Sliders` issues which are apparent in https://github.com/flutter/flutter/pull/147783. As a result, fixing the these bugs will unblock it. ### 1. Fixes the thumb doesn't align with `Slider` divisions.   ### 2. Fixes `RoundedRectSliderTrackShape` corners are not rendered correctly.  ### 3. Fixes round track shape corners when the thumb is at the start or end of the round track shape.   
This commit is contained in:
parent
82b63ff27d
commit
9e88446f89
@ -1650,7 +1650,22 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
sliderTheme: _sliderTheme,
|
||||
isDiscrete: isDiscrete,
|
||||
);
|
||||
final Offset thumbCenter = Offset(trackRect.left + visualPosition * trackRect.width, trackRect.center.dy);
|
||||
final double padding = isDiscrete || _sliderTheme.trackShape!.isRounded ? trackRect.height : 0.0;
|
||||
final double thumbPosition = isDiscrete
|
||||
? trackRect.left + visualPosition * (trackRect.width - padding) + padding / 2
|
||||
: trackRect.left + visualPosition * trackRect.width;
|
||||
// Apply padding to trackRect.left and trackRect.right if the track height is
|
||||
// greater than the thumb radius to ensure the thumb is drawn within the track.
|
||||
final Size thumbSize = _sliderTheme.thumbShape!.getPreferredSize(isInteractive, isDiscrete);
|
||||
final double thumbPadding = (padding > thumbSize.width / 2 ? padding / 2 : 0);
|
||||
final Offset thumbCenter = Offset(
|
||||
clampDouble(
|
||||
thumbPosition,
|
||||
trackRect.left + thumbPadding,
|
||||
trackRect.right - thumbPadding,
|
||||
),
|
||||
trackRect.center.dy,
|
||||
);
|
||||
if (isInteractive) {
|
||||
final Size overlaySize = sliderTheme.overlayShape!.getPreferredSize(isInteractive, false);
|
||||
overlayRect = Rect.fromCircle(center: thumbCenter, radius: overlaySize.width / 2.0);
|
||||
@ -1692,7 +1707,6 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
isEnabled: isInteractive,
|
||||
sliderTheme: _sliderTheme,
|
||||
).width;
|
||||
final double padding = trackRect.height;
|
||||
final double adjustedTrackWidth = trackRect.width - padding;
|
||||
// If the tick marks would be too dense, don't bother painting them.
|
||||
if (adjustedTrackWidth / divisions! >= 3.0 * tickMarkWidth) {
|
||||
|
@ -1117,6 +1117,11 @@ abstract class SliderTrackShape {
|
||||
bool isDiscrete,
|
||||
required TextDirection textDirection,
|
||||
});
|
||||
|
||||
/// Whether the track shape is rounded.
|
||||
///
|
||||
/// This is used to determine the correct position of the thumb in relation to the track.
|
||||
bool get isRounded => false;
|
||||
}
|
||||
|
||||
/// Base class for [RangeSlider] thumb shapes.
|
||||
@ -1534,6 +1539,10 @@ mixin BaseSliderTrackShape {
|
||||
// If the parentBox's size less than slider's size the trackRight will be less than trackLeft, so switch them.
|
||||
return Rect.fromLTRB(math.min(trackLeft, trackRight), trackTop, math.max(trackLeft, trackRight), trackBottom);
|
||||
}
|
||||
|
||||
/// Whether the track shape is rounded. This is used to determine the correct
|
||||
/// position of the thumb in relation to the track. Defaults to false.
|
||||
bool get isRounded => false;
|
||||
}
|
||||
|
||||
/// A [Slider] track that's a simple rectangle.
|
||||
@ -1714,39 +1723,45 @@ class RoundedRectSliderTrackShape extends SliderTrackShape with BaseSliderTrackS
|
||||
);
|
||||
final Radius trackRadius = Radius.circular(trackRect.height / 2);
|
||||
final Radius activeTrackRadius = Radius.circular((trackRect.height + additionalActiveTrackHeight) / 2);
|
||||
final bool isLTR = textDirection == TextDirection.ltr;
|
||||
final bool isRTL = textDirection == TextDirection.rtl;
|
||||
|
||||
context.canvas.drawRRect(
|
||||
RRect.fromLTRBAndCorners(
|
||||
trackRect.left,
|
||||
(textDirection == TextDirection.ltr) ? trackRect.top - (additionalActiveTrackHeight / 2): trackRect.top,
|
||||
thumbCenter.dx,
|
||||
(textDirection == TextDirection.ltr) ? trackRect.bottom + (additionalActiveTrackHeight / 2) : trackRect.bottom,
|
||||
topLeft: (textDirection == TextDirection.ltr) ? activeTrackRadius : trackRadius,
|
||||
bottomLeft: (textDirection == TextDirection.ltr) ? activeTrackRadius: trackRadius,
|
||||
),
|
||||
leftTrackPaint,
|
||||
);
|
||||
context.canvas.drawRRect(
|
||||
RRect.fromLTRBAndCorners(
|
||||
thumbCenter.dx,
|
||||
(textDirection == TextDirection.rtl) ? trackRect.top - (additionalActiveTrackHeight / 2) : trackRect.top,
|
||||
trackRect.right,
|
||||
(textDirection == TextDirection.rtl) ? trackRect.bottom + (additionalActiveTrackHeight / 2) : trackRect.bottom,
|
||||
topRight: (textDirection == TextDirection.rtl) ? activeTrackRadius : trackRadius,
|
||||
bottomRight: (textDirection == TextDirection.rtl) ? activeTrackRadius : trackRadius,
|
||||
),
|
||||
rightTrackPaint,
|
||||
);
|
||||
final bool drawInactiveTrack = thumbCenter.dx < (trackRect.right - (sliderTheme.trackHeight! / 2));
|
||||
if (drawInactiveTrack) {
|
||||
// Draw the inactive track segment.
|
||||
context.canvas.drawRRect(
|
||||
RRect.fromLTRBR(
|
||||
thumbCenter.dx - (sliderTheme.trackHeight! / 2),
|
||||
isRTL ? trackRect.top - (additionalActiveTrackHeight / 2) : trackRect.top,
|
||||
trackRect.right,
|
||||
isRTL ? trackRect.bottom + (additionalActiveTrackHeight / 2) : trackRect.bottom,
|
||||
isLTR ? trackRadius : activeTrackRadius,
|
||||
),
|
||||
rightTrackPaint,
|
||||
);
|
||||
}
|
||||
final bool drawActiveTrack = thumbCenter.dx > (trackRect.left + (sliderTheme.trackHeight! / 2));
|
||||
if (drawActiveTrack) {
|
||||
// Draw the active track segment.
|
||||
context.canvas.drawRRect(
|
||||
RRect.fromLTRBR(
|
||||
trackRect.left,
|
||||
isLTR ? trackRect.top - (additionalActiveTrackHeight / 2): trackRect.top,
|
||||
thumbCenter.dx + (sliderTheme.trackHeight! / 2),
|
||||
isLTR ? trackRect.bottom + (additionalActiveTrackHeight / 2) : trackRect.bottom,
|
||||
isLTR ? activeTrackRadius : trackRadius,
|
||||
),
|
||||
leftTrackPaint,
|
||||
);
|
||||
}
|
||||
|
||||
final bool showSecondaryTrack = (secondaryOffset != null) &&
|
||||
((textDirection == TextDirection.ltr)
|
||||
? (secondaryOffset.dx > thumbCenter.dx)
|
||||
: (secondaryOffset.dx < thumbCenter.dx));
|
||||
(isLTR ? (secondaryOffset.dx > thumbCenter.dx) : (secondaryOffset.dx < thumbCenter.dx));
|
||||
|
||||
if (showSecondaryTrack) {
|
||||
final ColorTween secondaryTrackColorTween = ColorTween(begin: sliderTheme.disabledSecondaryActiveTrackColor, end: sliderTheme.secondaryActiveTrackColor);
|
||||
final Paint secondaryTrackPaint = Paint()..color = secondaryTrackColorTween.evaluate(enableAnimation)!;
|
||||
if (textDirection == TextDirection.ltr) {
|
||||
if (isLTR) {
|
||||
context.canvas.drawRRect(
|
||||
RRect.fromLTRBAndCorners(
|
||||
thumbCenter.dx,
|
||||
@ -1773,6 +1788,9 @@ class RoundedRectSliderTrackShape extends SliderTrackShape with BaseSliderTrackS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isRounded => true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -558,7 +558,7 @@ void main() {
|
||||
await tester.tap(find.text('push wrapped'));
|
||||
await tester.pumpAndSettle(); // route animation
|
||||
RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
expect(sliderBox, paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor));
|
||||
expect(sliderBox, paints..rrect(color: inactiveTrackColor)..rrect(color: activeTrackColor));
|
||||
expect(sliderBox, paints..circle(color: thumbColor));
|
||||
|
||||
Navigator.of(navigatorContext).pop();
|
||||
@ -567,7 +567,7 @@ void main() {
|
||||
await tester.tap(find.text('push unwrapped'));
|
||||
await tester.pumpAndSettle(); // route animation
|
||||
sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
expect(sliderBox, isNot(paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor)));
|
||||
expect(sliderBox, isNot(paints..rrect(color: inactiveTrackColor)..rrect(color: activeTrackColor)));
|
||||
expect(sliderBox, isNot(paints..circle(color: thumbColor)));
|
||||
});
|
||||
|
||||
|
@ -167,7 +167,7 @@ void main() {
|
||||
|
||||
expect(value, equals(0.20));
|
||||
expect(log.length, 1);
|
||||
expect(log[0], const Offset(212.0, 300.0));
|
||||
expect(log[0], const Offset(213.0, 300.0));
|
||||
});
|
||||
|
||||
testWidgets('Slider can move when tapped (LTR)', (WidgetTester tester) async {
|
||||
@ -417,8 +417,8 @@ void main() {
|
||||
);
|
||||
|
||||
final List<Offset> expectedLog = <Offset>[
|
||||
const Offset(24.0, 300.0),
|
||||
const Offset(24.0, 300.0),
|
||||
const Offset(26.0, 300.0),
|
||||
const Offset(26.0, 300.0),
|
||||
const Offset(400.0, 300.0),
|
||||
];
|
||||
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(sliderKey)));
|
||||
@ -439,13 +439,13 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
expect(value, equals(0.0));
|
||||
expect(log.length, 7);
|
||||
expect(log.last.dx, moreOrLessEquals(344.5, epsilon: 0.1));
|
||||
expect(log.last.dx, moreOrLessEquals(344.8, epsilon: 0.1));
|
||||
// Final position.
|
||||
await tester.pump(const Duration(milliseconds: 80));
|
||||
expectedLog.add(const Offset(24.0, 300.0));
|
||||
expectedLog.add(const Offset(26.0, 300.0));
|
||||
expect(value, equals(0.0));
|
||||
expect(log.length, 8);
|
||||
expect(log.last.dx, moreOrLessEquals(24.0, epsilon: 0.1));
|
||||
expect(log.last.dx, moreOrLessEquals(26.0, epsilon: 0.1));
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
@ -490,7 +490,7 @@ void main() {
|
||||
expect(updates, equals(1));
|
||||
});
|
||||
|
||||
testWidgets('discrete Slider repaints when dragged', (WidgetTester tester) async {
|
||||
testWidgets('Discrete Slider repaints when dragged', (WidgetTester tester) async {
|
||||
final Key sliderKey = UniqueKey();
|
||||
double value = 0.0;
|
||||
final List<Offset> log = <Offset>[];
|
||||
@ -526,8 +526,8 @@ void main() {
|
||||
);
|
||||
|
||||
final List<Offset> expectedLog = <Offset>[
|
||||
const Offset(24.0, 300.0),
|
||||
const Offset(24.0, 300.0),
|
||||
const Offset(26.0, 300.0),
|
||||
const Offset(26.0, 300.0),
|
||||
const Offset(400.0, 300.0),
|
||||
];
|
||||
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(sliderKey)));
|
||||
@ -548,13 +548,13 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
expect(value, equals(0.0));
|
||||
expect(log.length, 7);
|
||||
expect(log.last.dx, moreOrLessEquals(344.5, epsilon: 0.1));
|
||||
expect(log.last.dx, moreOrLessEquals(344.8, epsilon: 0.1));
|
||||
// Final position.
|
||||
await tester.pump(const Duration(milliseconds: 80));
|
||||
expectedLog.add(const Offset(24.0, 300.0));
|
||||
expectedLog.add(const Offset(26.0, 300.0));
|
||||
expect(value, equals(0.0));
|
||||
expect(log.length, 8);
|
||||
expect(log.last.dx, moreOrLessEquals(24.0, epsilon: 0.1));
|
||||
expect(log.last.dx, moreOrLessEquals(26.0, epsilon: 0.1));
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
@ -1175,7 +1175,7 @@ void main() {
|
||||
..circle(x: 400.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 587.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 774.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 24.0, y: 24.0, radius: 10.0),
|
||||
..circle(x: 26.0, y: 24.0, radius: 10.0),
|
||||
);
|
||||
|
||||
gesture = await tester.startGesture(center);
|
||||
@ -1186,13 +1186,13 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..circle(x: 111.20703125, y: 24.0, radius: 5.687664985656738)
|
||||
..circle(x: 112.7431640625, y: 24.0, radius: 5.687664985656738)
|
||||
..circle(x: 26.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 213.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 400.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 587.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 774.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 111.20703125, y: 24.0, radius: 10.0),
|
||||
..circle(x: 112.7431640625, y: 24.0, radius: 10.0),
|
||||
);
|
||||
|
||||
// Reparenting in the middle of an animation should do nothing.
|
||||
@ -1206,13 +1206,13 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..circle(x: 190.0135726928711, y: 24.0, radius: 12.0)
|
||||
..circle(x: 191.130521774292, y: 24.0, radius: 12.0)
|
||||
..circle(x: 26.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 213.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 400.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 587.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 774.0, y: 24.0, radius: 1.0)
|
||||
..circle(x: 190.0135726928711, y: 24.0, radius: 10.0),
|
||||
..circle(x: 191.130521774292, y: 24.0, radius: 10.0),
|
||||
);
|
||||
// Wait for animations to finish.
|
||||
await tester.pumpAndSettle();
|
||||
@ -3249,11 +3249,11 @@ void main() {
|
||||
expect(
|
||||
renderObject,
|
||||
paints
|
||||
// active track RRect
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(-14.0, 2.0, 5.0, 8.0, topLeft: const Radius.circular(3.0), bottomLeft: const Radius.circular(3.0)))
|
||||
// inactive track RRect
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(5.0, 3.0, 24.0, 7.0, topRight: const Radius.circular(2.0), bottomRight: const Radius.circular(2.0)))
|
||||
// thumb
|
||||
// Inactive track RRect.
|
||||
..rrect(rrect: RRect.fromLTRBR(3.0, 3.0, 24.0, 7.0, const Radius.circular(2.0)))
|
||||
// Active track RRect.
|
||||
..rrect(rrect: RRect.fromLTRBR(-14.0, 2.0, 7.0, 8.0, const Radius.circular(3.0)))
|
||||
// Thumb.
|
||||
..circle(x: 5.0, y: 5.0, radius: 10.0, ),
|
||||
);
|
||||
});
|
||||
@ -3285,7 +3285,7 @@ void main() {
|
||||
await tester.pumpAndSettle(); // Finish the animation.
|
||||
|
||||
late RRect activeTrackRRect;
|
||||
expect(renderObject, paints..something((Symbol method, List<dynamic> arguments) {
|
||||
expect(renderObject, paints..rrect()..something((Symbol method, List<dynamic> arguments) {
|
||||
if (method != #drawRRect) {
|
||||
return false;
|
||||
}
|
||||
@ -3293,10 +3293,18 @@ void main() {
|
||||
return true;
|
||||
}));
|
||||
|
||||
const double padding = 4.0;
|
||||
// The thumb should at one-third(5 / 15) of the Slider.
|
||||
// The right of the active track shape is the position of the thumb.
|
||||
// 24.0 is the default margin, (800.0 - 24.0 - 24.0) is the slider's width.
|
||||
expect(nearEqual(activeTrackRRect.right, (800.0 - 24.0 - 24.0) * (5 / 15) + 24.0, 0.01), true);
|
||||
expect(
|
||||
nearEqual(
|
||||
activeTrackRRect.right,
|
||||
(800.0 - 24.0 - 24.0 + (padding / 2)) * (5 / 15) + 24.0 + padding / 2,
|
||||
0.01,
|
||||
),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider paints thumbColor', (WidgetTester tester) async {
|
||||
|
@ -165,12 +165,20 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 297.0, 362.4, 303.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: activeTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(362.4, 298.0, 776.0, 302.0, topRight: radius, bottomRight: radius), color: inactiveTrackColor),
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(360.4, 298.0, 776.0, 302.0, radius),
|
||||
color: inactiveTrackColor,
|
||||
)
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 297.0, 364.4, 303.0, activatedRadius),
|
||||
color: activeTrackColor,
|
||||
),
|
||||
);
|
||||
|
||||
// Test default colors for enabled slider.
|
||||
expect(material, paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: inactiveTrackColor)..rrect(color: activeTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..shadow(color: shadowColor));
|
||||
expect(material, paints..circle(color: thumbColor));
|
||||
expect(material, isNot(paints..circle(color: disabledThumbColor)));
|
||||
@ -182,7 +190,7 @@ void main() {
|
||||
|
||||
// Test defaults colors for discrete slider.
|
||||
await tester.pumpWidget(buildApp(divisions: 3));
|
||||
expect(material, paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: inactiveTrackColor)..rrect(color: activeTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
@ -204,8 +212,8 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(color: disabledActiveTrackColor)
|
||||
..rrect(color: disabledInactiveTrackColor)
|
||||
..rrect(color: disabledActiveTrackColor)
|
||||
..rrect(color: disabledSecondaryActiveTrackColor),
|
||||
);
|
||||
expect(material, paints..shadow(color: shadowColor)..circle(color: disabledThumbColor));
|
||||
@ -307,7 +315,7 @@ void main() {
|
||||
final RenderBox valueIndicatorBox = tester.renderObject(find.byType(Overlay));
|
||||
|
||||
// Check default theme for enabled widget.
|
||||
expect(material, paints..rrect(color: sliderTheme.activeTrackColor)..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: sliderTheme.activeTrackColor)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(material, paints..shadow(color: const Color(0xff000000)));
|
||||
expect(material, paints..circle(color: sliderTheme.thumbColor));
|
||||
expect(material, isNot(paints..circle(color: sliderTheme.disabledThumbColor)));
|
||||
@ -319,7 +327,7 @@ void main() {
|
||||
|
||||
// Test setting only the activeColor.
|
||||
await tester.pumpWidget(buildApp(activeColor: customColor1));
|
||||
expect(material, paints..rrect(color: customColor1)..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: customColor1)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(material, paints..shadow(color: Colors.black));
|
||||
expect(material, paints..circle(color: customColor1));
|
||||
expect(material, isNot(paints..circle(color: sliderTheme.thumbColor)));
|
||||
@ -330,7 +338,7 @@ void main() {
|
||||
|
||||
// Test setting only the inactiveColor.
|
||||
await tester.pumpWidget(buildApp(inactiveColor: customColor1));
|
||||
expect(material, paints..rrect(color: sliderTheme.activeTrackColor)..rrect(color: customColor1)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: customColor1)..rrect(color: sliderTheme.activeTrackColor)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(material, paints..shadow(color: Colors.black));
|
||||
expect(material, paints..circle(color: sliderTheme.thumbColor));
|
||||
expect(material, isNot(paints..circle(color: sliderTheme.disabledThumbColor)));
|
||||
@ -340,7 +348,7 @@ void main() {
|
||||
|
||||
// Test setting only the secondaryActiveColor.
|
||||
await tester.pumpWidget(buildApp(secondaryActiveColor: customColor1));
|
||||
expect(material, paints..rrect(color: sliderTheme.activeTrackColor)..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: customColor1));
|
||||
expect(material, paints..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: sliderTheme.activeTrackColor)..rrect(color: customColor1));
|
||||
expect(material, paints..shadow(color: Colors.black));
|
||||
expect(material, paints..circle(color: sliderTheme.thumbColor));
|
||||
expect(material, isNot(paints..circle(color: sliderTheme.disabledThumbColor)));
|
||||
@ -350,7 +358,7 @@ void main() {
|
||||
|
||||
// Test setting both activeColor, inactiveColor, and secondaryActiveColor.
|
||||
await tester.pumpWidget(buildApp(activeColor: customColor1, inactiveColor: customColor2, secondaryActiveColor: customColor3));
|
||||
expect(material, paints..rrect(color: customColor1)..rrect(color: customColor2)..rrect(color: customColor3));
|
||||
expect(material, paints..rrect(color: customColor2)..rrect(color: customColor1)..rrect(color: customColor3));
|
||||
expect(material, paints..shadow(color: Colors.black));
|
||||
expect(material, paints..circle(color: customColor1));
|
||||
expect(material, isNot(paints..circle(color: sliderTheme.thumbColor)));
|
||||
@ -361,7 +369,7 @@ void main() {
|
||||
|
||||
// Test colors for discrete slider.
|
||||
await tester.pumpWidget(buildApp(divisions: 3));
|
||||
expect(material, paints..rrect(color: sliderTheme.activeTrackColor)..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: sliderTheme.inactiveTrackColor)..rrect(color: sliderTheme.activeTrackColor)..rrect(color: sliderTheme.secondaryActiveTrackColor));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
@ -384,7 +392,7 @@ void main() {
|
||||
secondaryActiveColor: customColor3,
|
||||
divisions: 3,
|
||||
));
|
||||
expect(material, paints..rrect(color: customColor1)..rrect(color: customColor2)..rrect(color: customColor3));
|
||||
expect(material, paints..rrect(color: customColor2)..rrect(color: customColor1)..rrect(color: customColor3));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
@ -409,8 +417,8 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(color: sliderTheme.disabledInactiveTrackColor)
|
||||
..rrect(color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(color: sliderTheme.disabledSecondaryActiveTrackColor),
|
||||
);
|
||||
expect(material, paints..shadow(color: Colors.black)..circle(color: sliderTheme.disabledThumbColor));
|
||||
@ -443,8 +451,8 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(color: sliderTheme.disabledInactiveTrackColor)
|
||||
..rrect(color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(color: sliderTheme.disabledSecondaryActiveTrackColor),
|
||||
);
|
||||
expect(material, paints..circle(color: sliderTheme.disabledThumbColor));
|
||||
@ -485,8 +493,8 @@ void main() {
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect(color: const Color(0xfffafafa))
|
||||
..rrect(color: customColor1) // active track
|
||||
..rrect(color: customColor2) // inactive track
|
||||
..rrect(color: customColor2) // Inactive track
|
||||
..rrect(color: customColor1) // Active track
|
||||
..circle(color: customColor1.withOpacity(0.12)) // overlay
|
||||
..circle(color: customColor2) // 1st tick mark
|
||||
..circle(color: customColor2) // 2nd tick mark
|
||||
@ -549,7 +557,7 @@ void main() {
|
||||
final MaterialInkController material = Material.of(tester.element(find.byType(Slider)));
|
||||
|
||||
// Test Slider parameters.
|
||||
expect(material, paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: inactiveTrackColor)..rrect(color: activeTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..circle(color: thumbColor));
|
||||
} finally {
|
||||
debugDisableShadows = true;
|
||||
@ -712,9 +720,23 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 297.0, 212.0, 303.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.activeTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 776.0, 302.0, topRight: radius, bottomRight: radius), color: sliderTheme.inactiveTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 400.0, 302.0, topRight: radius, bottomRight: radius), color: sliderTheme.secondaryActiveTrackColor),
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(210.0, 298.0, 776.0, 302.0, radius),
|
||||
color: sliderTheme.inactiveTrackColor,
|
||||
)
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 297.0, 214.0, 303.0, activatedRadius),
|
||||
color: sliderTheme.activeTrackColor,
|
||||
)
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 400.0, 302.0,
|
||||
topRight: radius,
|
||||
bottomRight: radius,
|
||||
),
|
||||
color: sliderTheme.secondaryActiveTrackColor,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, secondaryTrackValue: 0.5, enabled: false));
|
||||
@ -724,9 +746,23 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 297.0, 212.0, 303.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 776.0, 302.0, topRight: radius, bottomRight: radius), color: sliderTheme.disabledInactiveTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 400.0, 302.0, topRight: radius, bottomRight: radius), color: sliderTheme.disabledSecondaryActiveTrackColor),
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(210.0, 298.0, 776.0, 302.0, radius),
|
||||
color: sliderTheme.disabledInactiveTrackColor,
|
||||
)
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 297.0, 214.0, 303.0, activatedRadius),
|
||||
color: sliderTheme.disabledActiveTrackColor,
|
||||
)
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 400.0, 302.0,
|
||||
topRight: radius,
|
||||
bottomRight: radius,
|
||||
),
|
||||
color: sliderTheme.disabledSecondaryActiveTrackColor,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1276,8 +1312,16 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 291.0, 212.0, 309.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.activeTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 292.0, 776.0, 308.0, topRight: radius, bottomRight: radius), color: sliderTheme.inactiveTrackColor),
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(204.0, 292.0, 776.0, 308.0, radius),
|
||||
color: sliderTheme.inactiveTrackColor,
|
||||
)
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 291.0, 220.0, 309.0, activatedRadius),
|
||||
color: sliderTheme.activeTrackColor,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
|
||||
@ -1288,8 +1332,16 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 291.0, 212.0, 309.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 292.0, 776.0, 308.0, topRight: radius, bottomRight: radius), color: sliderTheme.disabledInactiveTrackColor),
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(204.0, 292.0, 776.0, 308.0, radius),
|
||||
color: sliderTheme.disabledInactiveTrackColor,
|
||||
)
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 291.0, 220.0, 309.0, activatedRadius),
|
||||
color: sliderTheme.disabledActiveTrackColor,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1422,23 +1474,21 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
// active track RRect. Starts 10 pixels from left of screen.
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(
|
||||
10.0,
|
||||
297.0,
|
||||
400.0,
|
||||
303.0,
|
||||
topLeft: const Radius.circular(3.0),
|
||||
bottomLeft: const Radius.circular(3.0),
|
||||
))
|
||||
// inactive track RRect. Ends 10 pixels from right of screen.
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(
|
||||
400.0,
|
||||
// Inactive track RRect. Ends 10 pixels from right of screen.
|
||||
..rrect(rrect: RRect.fromLTRBR(
|
||||
398.0,
|
||||
298.0,
|
||||
790.0,
|
||||
302.0,
|
||||
topRight: const Radius.circular(2.0),
|
||||
bottomRight: const Radius.circular(2.0),
|
||||
const Radius.circular(2.0),
|
||||
))
|
||||
// Active track RRect. Starts 10 pixels from left of screen.
|
||||
..rrect(rrect: RRect.fromLTRBR(
|
||||
10.0,
|
||||
297.0,
|
||||
402.0,
|
||||
303.0,
|
||||
const Radius.circular(3.0),
|
||||
))
|
||||
// The thumb.
|
||||
..circle(x: 400.0, y: 300.0, radius: 10.0),
|
||||
@ -1882,11 +1932,12 @@ void main() {
|
||||
|
||||
testWidgets('activeTrackRadius is taken into account when painting the border of the active track', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(_buildApp(
|
||||
value: 0.5,
|
||||
ThemeData().sliderTheme.copyWith(
|
||||
trackShape: const RoundedRectSliderTrackShapeWithCustomAdditionalActiveTrackHeight(
|
||||
additionalActiveTrackHeight: 10.0
|
||||
)
|
||||
)
|
||||
additionalActiveTrackHeight: 10.0,
|
||||
),
|
||||
),
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
final Offset center = tester.getCenter(find.byType(Slider));
|
||||
@ -1894,16 +1945,10 @@ void main() {
|
||||
expect(
|
||||
find.byType(Slider),
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(
|
||||
24.0, 293.0, 24.0, 307.0,
|
||||
topLeft: const Radius.circular(7.0),
|
||||
bottomLeft: const Radius.circular(7.0),
|
||||
))
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(
|
||||
24.0, 298.0, 776.0, 302.0,
|
||||
topRight: const Radius.circular(2.0),
|
||||
bottomRight: const Radius.circular(2.0),
|
||||
)),
|
||||
// Inactive track.
|
||||
..rrect(rrect: RRect.fromLTRBR(398.0, 298.0, 776.0, 302.0, const Radius.circular(2.0)))
|
||||
// Active track.
|
||||
..rrect(rrect: RRect.fromLTRBR(24.0, 293.0, 402.0, 307.0, const Radius.circular(7.0))),
|
||||
);
|
||||
|
||||
// Finish gesture to release resources.
|
||||
@ -2078,8 +2123,8 @@ void main() {
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect(color: const Color(0xfffef7ff))
|
||||
..rrect(color: const Color(0xff6750a4))
|
||||
..rrect(color: const Color(0xffe6e0e9))
|
||||
..rrect(color: const Color(0xff6750a4))
|
||||
..path(color: Color(theme.colorScheme.primary.value))
|
||||
);
|
||||
|
||||
@ -2434,6 +2479,75 @@ void main() {
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
group('RoundedRectSliderTrackShape', () {
|
||||
testWidgets('Only draw active track if thumb center is higher than trackRect.left and track radius', (WidgetTester tester) async {
|
||||
const SliderThemeData sliderTheme = SliderThemeData(trackShape: RoundedRectSliderTrackShape());
|
||||
await tester.pumpWidget(_buildApp(sliderTheme));
|
||||
|
||||
MaterialInkController material = Material.of(tester.element(find.byType(Slider)));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(22.0, 298.0, 776.0, 302.0, const Radius.circular(2.0)),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.025));
|
||||
|
||||
material = Material.of(tester.element(find.byType(Slider)));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(40.8, 298.0, 776.0, 302.0, const Radius.circular(2.0)),
|
||||
)
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 297.0, 44.8, 303.0, const Radius.circular(3.0)),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Only draw inactive track if thumb center is lower than trackRect.right and track radius', (WidgetTester tester) async {
|
||||
const SliderThemeData sliderTheme = SliderThemeData(trackShape: RoundedRectSliderTrackShape());
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 1.0));
|
||||
|
||||
MaterialInkController material = Material.of(tester.element(find.byType(Slider)));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 297.0, 778.0, 303.0, const Radius.circular(3.0)),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.975));
|
||||
|
||||
material = Material.of(tester.element(find.byType(Slider)));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
// Inactive track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(755.2, 298.0, 776.0, 302.0, const Radius.circular(2.0)),
|
||||
)
|
||||
// Active track.
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 297.0, 759.2, 303.0, const Radius.circular(3.0)),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('SliderTrackShape isRounded defaults', (WidgetTester tester) async {
|
||||
expect(const RectangularSliderTrackShape().isRounded, isFalse);
|
||||
expect(const RoundedRectSliderTrackShape().isRounded, isTrue);
|
||||
});
|
||||
|
||||
group('Material 2', () {
|
||||
// These tests are only relevant for Material 2. Once Material 2
|
||||
// support is deprecated and the APIs are removed, these tests
|
||||
@ -2500,12 +2614,17 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 297.0, 362.4, 303.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: activeTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(362.4, 298.0, 776.0, 302.0, topRight: radius, bottomRight: radius), color: inactiveTrackColor),
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(360.4, 298.0, 776.0, 302.0, radius),
|
||||
color: inactiveTrackColor)
|
||||
..rrect(
|
||||
rrect: RRect.fromLTRBR(24.0, 297.0, 364.4, 303.0, activatedRadius),
|
||||
color: activeTrackColor,
|
||||
),
|
||||
);
|
||||
|
||||
// Test default colors for enabled slider.
|
||||
expect(material, paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: inactiveTrackColor)..rrect(color: activeTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..shadow(color: shadowColor));
|
||||
expect(material, paints..circle(color: thumbColor));
|
||||
expect(material, isNot(paints..circle(color: disabledThumbColor)));
|
||||
@ -2517,7 +2636,7 @@ void main() {
|
||||
|
||||
// Test defaults colors for discrete slider.
|
||||
await tester.pumpWidget(buildApp(divisions: 3));
|
||||
expect(material, paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(material, paints..rrect(color: inactiveTrackColor)..rrect(color: activeTrackColor)..rrect(color: secondaryActiveTrackColor));
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
@ -2539,8 +2658,8 @@ void main() {
|
||||
expect(
|
||||
material,
|
||||
paints
|
||||
..rrect(color: disabledActiveTrackColor)
|
||||
..rrect(color: disabledInactiveTrackColor)
|
||||
..rrect(color: disabledActiveTrackColor)
|
||||
..rrect(color: disabledSecondaryActiveTrackColor),
|
||||
);
|
||||
expect(material, paints..shadow(color: Colors.black)..circle(color: disabledThumbColor));
|
||||
@ -2634,8 +2753,8 @@ void main() {
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect(color: const Color(0xfffafafa))
|
||||
..rrect(color: const Color(0xff2196f3))
|
||||
..rrect(color: const Color(0x3d2196f3))
|
||||
..rrect(color: const Color(0xff2196f3))
|
||||
// Test that the value indicator text is painted with the correct color.
|
||||
..path(color: const Color(0xf55f5f5f))
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user