[Material] Simple API for skipping over certain Slider shapes (#27613)
* Provide a simmplified API for skipping over slider thumb, overlay, and tick mark painting * doc fixes * comments * comments * comments * comments * comments * analyzer * comments
This commit is contained in:
parent
20d4770136
commit
39bb6712f6
@ -154,6 +154,16 @@ enum ShowValueIndicator {
|
|||||||
/// [RoundSliderTickMarkShape], [PaddleSliderValueIndicatorShape], and
|
/// [RoundSliderTickMarkShape], [PaddleSliderValueIndicatorShape], and
|
||||||
/// [RoundSliderOverlayShape] for examples.
|
/// [RoundSliderOverlayShape] for examples.
|
||||||
///
|
///
|
||||||
|
/// The track painting can be skipped by specifying 0 for [trackHeight].
|
||||||
|
/// The thumb painting can be skipped by specifying
|
||||||
|
/// [SliderComponentShape.noThumb] for [SliderThemeData.thumbShape].
|
||||||
|
/// The overlay painting can be skipped by specifying
|
||||||
|
/// [SliderComponentShape.noOverlay] for [SliderThemeData.overlayShape].
|
||||||
|
/// The tick mark painting can be skipped by specifying
|
||||||
|
/// [SliderTickMarkShape.noTickMark] for [SliderThemeData.tickMarkShape].
|
||||||
|
/// The value indicator painting can be skipped by specifying the
|
||||||
|
/// appropriate [ShowValueIndicator] for [SliderThemeData.showValueIndicator].
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [SliderTheme] widget, which can override the slider theme of its
|
/// * [SliderTheme] widget, which can override the slider theme of its
|
||||||
@ -706,6 +716,9 @@ abstract class SliderTrackShape {
|
|||||||
/// This is a simplified version of [SliderComponentShape] with a
|
/// This is a simplified version of [SliderComponentShape] with a
|
||||||
/// [SliderThemeData] passed when getting the preferred size.
|
/// [SliderThemeData] passed when getting the preferred size.
|
||||||
///
|
///
|
||||||
|
/// The tick mark painting can be skipped by specifying [noTickMark] for
|
||||||
|
/// [SliderThemeData.tickMarkShape].
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [RoundSliderTickMarkShape] for a simple example of a tick mark shape.
|
/// * [RoundSliderTickMarkShape] for a simple example of a tick mark shape.
|
||||||
@ -759,6 +772,45 @@ abstract class SliderTickMarkShape {
|
|||||||
bool isEnabled,
|
bool isEnabled,
|
||||||
TextDirection textDirection,
|
TextDirection textDirection,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Special instance of [SliderTickMarkShape] to skip the tick mark painting.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [SliderThemeData.tickMarkShape], which is the shape that the [Slider]
|
||||||
|
/// uses when painting tick marks.
|
||||||
|
static final SliderTickMarkShape noTickMark = _EmptySliderTickMarkShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A special version of [SliderTickMarkShape] that has a zero size and paints
|
||||||
|
/// nothing.
|
||||||
|
///
|
||||||
|
/// This class is used to create a special instance of a [SliderTickMarkShape]
|
||||||
|
/// that will not paint any tick mark shape. A static reference is stored in
|
||||||
|
/// [SliderTickMarkShape.noTickMark]. When this value is specified for
|
||||||
|
/// [SliderThemeData.tickMarkShape], the tick mark painting is skipped.
|
||||||
|
class _EmptySliderTickMarkShape extends SliderTickMarkShape {
|
||||||
|
@override
|
||||||
|
Size getPreferredSize({
|
||||||
|
SliderThemeData sliderTheme,
|
||||||
|
bool isEnabled,
|
||||||
|
}) {
|
||||||
|
return Size.zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(
|
||||||
|
PaintingContext context,
|
||||||
|
Offset center, {
|
||||||
|
RenderBox parentBox,
|
||||||
|
SliderThemeData sliderTheme,
|
||||||
|
Animation<double> enableAnimation,
|
||||||
|
Offset thumbCenter,
|
||||||
|
bool isEnabled,
|
||||||
|
TextDirection textDirection,
|
||||||
|
}) {
|
||||||
|
// no-op.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Base class for slider thumb, thumb overlay, and value indicator shapes.
|
/// Base class for slider thumb, thumb overlay, and value indicator shapes.
|
||||||
@ -768,6 +820,12 @@ abstract class SliderTickMarkShape {
|
|||||||
/// All shapes are painted to the same canvas and ordering is important.
|
/// All shapes are painted to the same canvas and ordering is important.
|
||||||
/// The overlay is painted first, then the value indicator, then the thumb.
|
/// The overlay is painted first, then the value indicator, then the thumb.
|
||||||
///
|
///
|
||||||
|
/// The thumb painting can be skipped by specifying [noThumb] for
|
||||||
|
/// [SliderThemeData.thumbShape].
|
||||||
|
///
|
||||||
|
/// The overlay painting can be skipped by specifying [noOverlay] for
|
||||||
|
/// [SliderThemeData.overlayShape].
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [RoundSliderThumbShape], which is the the default thumb shape.
|
/// * [RoundSliderThumbShape], which is the the default thumb shape.
|
||||||
@ -821,6 +879,52 @@ abstract class SliderComponentShape {
|
|||||||
TextDirection textDirection,
|
TextDirection textDirection,
|
||||||
double value,
|
double value,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Special instance of [SliderComponentShape] to skip the thumb drawing.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [SliderThemeData.thumbShape], which is the shape that the [Slider]
|
||||||
|
/// uses when painting the thumb.
|
||||||
|
static final SliderComponentShape noThumb = _EmptySliderComponentShape();
|
||||||
|
|
||||||
|
/// Special instance of [SliderComponentShape] to skip the overlay drawing.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [SliderThemeData.overlayShape], which is the shape that the [Slider]
|
||||||
|
/// uses when painting the overlay.
|
||||||
|
static final SliderComponentShape noOverlay = _EmptySliderComponentShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A special version of [SliderComponentShape] that has a zero size and paints
|
||||||
|
/// nothing.
|
||||||
|
///
|
||||||
|
/// This class is used to create a special instance of a [SliderComponentShape]
|
||||||
|
/// that will not paint any component shape. A static reference is stored in
|
||||||
|
/// [SliderTickMarkShape.noThumb] and [SliderTickMarkShape.noOverlay]. When this value
|
||||||
|
/// is specified for [SliderThemeData.thumbShape], the thumb painting is
|
||||||
|
/// skipped. When this value is specified for [SliderThemeData.overlaySHape],
|
||||||
|
/// the overlay painting is skipped.
|
||||||
|
class _EmptySliderComponentShape extends SliderComponentShape {
|
||||||
|
@override
|
||||||
|
Size getPreferredSize(bool isEnabled, bool isDiscrete) => Size.zero;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(
|
||||||
|
PaintingContext context,
|
||||||
|
Offset center, {
|
||||||
|
Animation<double> activationAnimation,
|
||||||
|
Animation<double> enableAnimation,
|
||||||
|
bool isDiscrete,
|
||||||
|
TextPainter labelPainter,
|
||||||
|
RenderBox parentBox,
|
||||||
|
SliderThemeData sliderTheme,
|
||||||
|
TextDirection textDirection,
|
||||||
|
double value,
|
||||||
|
}) {
|
||||||
|
// no-op.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following shapes are the material defaults.
|
// The following shapes are the material defaults.
|
||||||
@ -894,6 +998,12 @@ class RectangularSliderTrackShape extends SliderTrackShape {
|
|||||||
bool isDiscrete,
|
bool isDiscrete,
|
||||||
bool isEnabled,
|
bool isEnabled,
|
||||||
}) {
|
}) {
|
||||||
|
// If the slider track height is 0, then it makes no difference whether the
|
||||||
|
// track is painted or not, therefore the painting can be a no-op.
|
||||||
|
if (sliderTheme.trackHeight == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Assign the track segment paints, which are left: active, right: inactive,
|
// Assign the track segment paints, which are left: active, right: inactive,
|
||||||
// but reversed for right to left text.
|
// but reversed for right to left text.
|
||||||
final ColorTween activeTrackColorTween = ColorTween(begin: sliderTheme.disabledActiveTrackColor , end: sliderTheme.activeTrackColor);
|
final ColorTween activeTrackColorTween = ColorTween(begin: sliderTheme.disabledActiveTrackColor , end: sliderTheme.activeTrackColor);
|
||||||
|
@ -590,6 +590,152 @@ void main() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Only the thumb, overlay, and tick mark have special shortcuts to provide
|
||||||
|
// no-op or empty shapes.
|
||||||
|
//
|
||||||
|
// The track can also be skipped by providing 0 height.
|
||||||
|
//
|
||||||
|
// The value indicator can be skipped by passing the appropriate
|
||||||
|
// [ShowValueIndicator].
|
||||||
|
testWidgets('The slider can skip all of its comoponent painting', (WidgetTester tester) async {
|
||||||
|
// Pump a slider with all shapes skipped.
|
||||||
|
await tester.pumpWidget(_buildApp(
|
||||||
|
ThemeData().sliderTheme.copyWith(
|
||||||
|
trackHeight: 0,
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
thumbShape: SliderComponentShape.noThumb,
|
||||||
|
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||||
|
showValueIndicator: ShowValueIndicator.never,
|
||||||
|
),
|
||||||
|
value: 0.5,
|
||||||
|
divisions: 4
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
expect(sliderBox, paintsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The slider can skip all component painting except the track', (WidgetTester tester) async {
|
||||||
|
// Pump a slider with just a track.
|
||||||
|
await tester.pumpWidget(_buildApp(
|
||||||
|
ThemeData().sliderTheme.copyWith(
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
thumbShape: SliderComponentShape.noThumb,
|
||||||
|
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||||
|
showValueIndicator: ShowValueIndicator.never,
|
||||||
|
),
|
||||||
|
value: 0.5,
|
||||||
|
divisions: 4
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
// Only 2 track segments.
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawRect, 2));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawCircle, 0));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The slider can skip all component painting except the tick marks', (WidgetTester tester) async {
|
||||||
|
// Pump a slider with just tick marks.
|
||||||
|
await tester.pumpWidget(_buildApp(
|
||||||
|
ThemeData().sliderTheme.copyWith(
|
||||||
|
trackHeight: 0,
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
thumbShape: SliderComponentShape.noThumb,
|
||||||
|
showValueIndicator: ShowValueIndicator.never,
|
||||||
|
),
|
||||||
|
value: 0.5,
|
||||||
|
divisions: 4
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
// Only 5 tick marks.
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawRect, 0));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawCircle, 5));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The slider can skip all component painting except the thumb', (WidgetTester tester) async {
|
||||||
|
// Pump a slider with just a thumb.
|
||||||
|
await tester.pumpWidget(_buildApp(
|
||||||
|
ThemeData().sliderTheme.copyWith(
|
||||||
|
trackHeight: 0,
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||||
|
showValueIndicator: ShowValueIndicator.never,
|
||||||
|
),
|
||||||
|
value: 0.5,
|
||||||
|
divisions: 4
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
// Only 1 thumb.
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawRect, 0));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawCircle, 1));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The slider can skip all component painting except the overlay', (WidgetTester tester) async {
|
||||||
|
// Pump a slider with just an overlay.
|
||||||
|
await tester.pumpWidget(_buildApp(
|
||||||
|
ThemeData().sliderTheme.copyWith(
|
||||||
|
trackHeight: 0,
|
||||||
|
thumbShape: SliderComponentShape.noThumb,
|
||||||
|
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||||
|
showValueIndicator: ShowValueIndicator.never,
|
||||||
|
),
|
||||||
|
value: 0.5,
|
||||||
|
divisions: 4
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
// Tap the center of the track and wait for animations to finish.
|
||||||
|
final Offset center = tester.getCenter(find.byType(Slider));
|
||||||
|
final TestGesture gesture = await tester.startGesture(center);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// Only 1 overlay.
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawRect, 0));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawCircle, 1));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The slider can skip all component painting except the value indicator', (WidgetTester tester) async {
|
||||||
|
// Pump a slider with just a value indicator.
|
||||||
|
await tester.pumpWidget(_buildApp(
|
||||||
|
ThemeData().sliderTheme.copyWith(
|
||||||
|
trackHeight: 0,
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
thumbShape: SliderComponentShape.noThumb,
|
||||||
|
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||||
|
showValueIndicator: ShowValueIndicator.always,
|
||||||
|
),
|
||||||
|
value: 0.5,
|
||||||
|
divisions: 4
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
// Tap the center of the track and wait for animations to finish.
|
||||||
|
final Offset center = tester.getCenter(find.byType(Slider));
|
||||||
|
final TestGesture gesture = await tester.startGesture(center);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// Only 1 value indicator.
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawRect, 0));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawCircle, 0));
|
||||||
|
expect(sliderBox, paintsExactlyCountTimes(#drawPath, 1));
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildApp(
|
Widget _buildApp(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user