Unverified Commit 96f15c74 authored by Anthony's avatar Anthony Committed by GitHub

[Material] Update slider and slider theme with new sizes, shapes, and color...

[Material] Update slider and slider theme with new sizes, shapes, and color mappings (2nd attempt) (#31564)

#30390 was rolled back. This PR will re-roll it forward.

This PR makes a number of changes to the visual appearance of material sliders:

Sizes/Shapes
** enabled thumb radius from 6 to 10
** disabled thumb radius from 4 to 10 with no gap
** default track shape is a rounded rect rather than a rect
**
Colors
** all of the colors now use the new color scheme
** overlay opacity has been reduce from 16% to 12%
** value indicator text color now respects the indicator it is on by using onPrimary
** disabledThumb color no respects the surface it is on by using onSurface
The slider theme is also now constructed consistently with other theme objects within the ThemeData. By default, all values are null, and have default values that are resolved in the slider itself, rather than in the slider theme.
parent b27b3d74
...@@ -428,27 +428,52 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin { ...@@ -428,27 +428,52 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
return widget.max > widget.min ? (value - widget.min) / (widget.max - widget.min) : 0.0; return widget.max > widget.min ? (value - widget.min) / (widget.max - widget.min) : 0.0;
} }
static const double _defaultTrackHeight = 2;
static const SliderTrackShape _defaultTrackShape = RoundedRectSliderTrackShape();
static const SliderTickMarkShape _defaultTickMarkShape = RoundSliderTickMarkShape();
static const SliderComponentShape _defaultOverlayShape = RoundSliderOverlayShape();
static const SliderComponentShape _defaultThumbShape = RoundSliderThumbShape();
static const SliderComponentShape _defaultValueIndicatorShape = PaddleSliderValueIndicatorShape();
static const ShowValueIndicator _defaultShowValueIndicator = ShowValueIndicator.onlyForDiscrete;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
final ThemeData theme = Theme.of(context);
SliderThemeData sliderTheme = SliderTheme.of(context); SliderThemeData sliderTheme = SliderTheme.of(context);
// If the widget has active or inactive colors specified, then we plug them // If the widget has active or inactive colors specified, then we plug them
// in to the slider theme as best we can. If the developer wants more // in to the slider theme as best we can. If the developer wants more
// control than that, then they need to use a SliderTheme. // control than that, then they need to use a SliderTheme. The default
if (widget.activeColor != null || widget.inactiveColor != null) { // colors come from the ThemeData.colorScheme. These colors, along with
sliderTheme = sliderTheme.copyWith( // the default shapes and text styles are aligned to the Material
activeTrackColor: widget.activeColor, // Guidelines.
inactiveTrackColor: widget.inactiveColor, sliderTheme = sliderTheme.copyWith(
activeTickMarkColor: widget.inactiveColor, trackHeight: sliderTheme.trackHeight ?? _defaultTrackHeight,
inactiveTickMarkColor: widget.activeColor, activeTrackColor: widget.activeColor ?? sliderTheme.activeTrackColor ?? theme.colorScheme.primary,
thumbColor: widget.activeColor, inactiveTrackColor: widget.inactiveColor ?? sliderTheme.inactiveTrackColor ?? theme.colorScheme.primary.withOpacity(0.24),
valueIndicatorColor: widget.activeColor, disabledActiveTrackColor: sliderTheme.disabledActiveTrackColor ?? theme.colorScheme.onSurface.withOpacity(0.32),
overlayColor: widget.activeColor?.withAlpha(0x29), disabledInactiveTrackColor: sliderTheme.disabledInactiveTrackColor ?? theme.colorScheme.onSurface.withOpacity(0.12),
); activeTickMarkColor: widget.inactiveColor ?? sliderTheme.activeTickMarkColor ?? theme.colorScheme.onPrimary.withOpacity(0.54),
} inactiveTickMarkColor: widget.activeColor ?? sliderTheme.inactiveTickMarkColor ?? theme.colorScheme.primary.withOpacity(0.54),
disabledActiveTickMarkColor: sliderTheme.disabledActiveTickMarkColor ?? theme.colorScheme.onPrimary.withOpacity(0.12),
disabledInactiveTickMarkColor: sliderTheme.disabledInactiveTickMarkColor ?? theme.colorScheme.onSurface.withOpacity(0.12),
thumbColor: widget.activeColor ?? sliderTheme.thumbColor ?? theme.colorScheme.primary,
disabledThumbColor: sliderTheme.disabledThumbColor ?? theme.colorScheme.onSurface.withOpacity(0.38),
overlayColor: widget.activeColor?.withOpacity(0.12) ?? sliderTheme.overlayColor ?? theme.colorScheme.primary.withOpacity(0.12),
valueIndicatorColor: widget.activeColor ?? sliderTheme.valueIndicatorColor ?? theme.colorScheme.primary,
trackShape: sliderTheme.trackShape ?? _defaultTrackShape,
tickMarkShape: sliderTheme.tickMarkShape ?? _defaultTickMarkShape,
thumbShape: sliderTheme.thumbShape ?? _defaultThumbShape,
overlayShape: sliderTheme.overlayShape ?? _defaultOverlayShape,
valueIndicatorShape: sliderTheme.valueIndicatorShape ?? _defaultValueIndicatorShape,
showValueIndicator: sliderTheme.showValueIndicator ?? _defaultShowValueIndicator,
valueIndicatorTextStyle: sliderTheme.valueIndicatorTextStyle ?? theme.textTheme.body2.copyWith(
color: theme.colorScheme.onPrimary,
),
);
return _SliderRenderObjectWidget( return _SliderRenderObjectWidget(
value: _unlerp(widget.value), value: _unlerp(widget.value),
...@@ -498,7 +523,6 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { ...@@ -498,7 +523,6 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
divisions: divisions, divisions: divisions,
label: label, label: label,
sliderTheme: sliderTheme, sliderTheme: sliderTheme,
theme: Theme.of(context),
mediaQueryData: mediaQueryData, mediaQueryData: mediaQueryData,
onChanged: onChanged, onChanged: onChanged,
onChangeStart: onChangeStart, onChangeStart: onChangeStart,
...@@ -536,7 +560,6 @@ class _RenderSlider extends RenderBox { ...@@ -536,7 +560,6 @@ class _RenderSlider extends RenderBox {
int divisions, int divisions,
String label, String label,
SliderThemeData sliderTheme, SliderThemeData sliderTheme,
ThemeData theme,
MediaQueryData mediaQueryData, MediaQueryData mediaQueryData,
TargetPlatform platform, TargetPlatform platform,
ValueChanged<double> onChanged, ValueChanged<double> onChanged,
...@@ -554,7 +577,6 @@ class _RenderSlider extends RenderBox { ...@@ -554,7 +577,6 @@ class _RenderSlider extends RenderBox {
_value = value, _value = value,
_divisions = divisions, _divisions = divisions,
_sliderTheme = sliderTheme, _sliderTheme = sliderTheme,
_theme = theme,
_mediaQueryData = mediaQueryData, _mediaQueryData = mediaQueryData,
_onChanged = onChanged, _onChanged = onChanged,
_state = state, _state = state,
...@@ -983,7 +1005,6 @@ class _RenderSlider extends RenderBox { ...@@ -983,7 +1005,6 @@ class _RenderSlider extends RenderBox {
isEnabled: isInteractive, isEnabled: isInteractive,
); );
// TODO(closkmith): Move this to paint after the thumb.
if (!_overlayAnimation.isDismissed) { if (!_overlayAnimation.isDismissed) {
_sliderTheme.overlayShape.paint( _sliderTheme.overlayShape.paint(
context, context,
...@@ -1000,7 +1021,6 @@ class _RenderSlider extends RenderBox { ...@@ -1000,7 +1021,6 @@ class _RenderSlider extends RenderBox {
} }
if (isDiscrete) { if (isDiscrete) {
// TODO(clocksmith): Align tick mark centers to ends of track by not subtracting diameter from length.
final double tickMarkWidth = _sliderTheme.tickMarkShape.getPreferredSize( final double tickMarkWidth = _sliderTheme.tickMarkShape.getPreferredSize(
isEnabled: isInteractive, isEnabled: isInteractive,
sliderTheme: _sliderTheme, sliderTheme: _sliderTheme,
......
...@@ -210,46 +210,27 @@ class SliderThemeData extends Diagnosticable { ...@@ -210,46 +210,27 @@ class SliderThemeData extends Diagnosticable {
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
const SliderThemeData({ const SliderThemeData({
@required this.trackHeight, this.trackHeight,
@required this.activeTrackColor, this.activeTrackColor,
@required this.inactiveTrackColor, this.inactiveTrackColor,
@required this.disabledActiveTrackColor, this.disabledActiveTrackColor,
@required this.disabledInactiveTrackColor, this.disabledInactiveTrackColor,
@required this.activeTickMarkColor, this.activeTickMarkColor,
@required this.inactiveTickMarkColor, this.inactiveTickMarkColor,
@required this.disabledActiveTickMarkColor, this.disabledActiveTickMarkColor,
@required this.disabledInactiveTickMarkColor, this.disabledInactiveTickMarkColor,
@required this.thumbColor, this.thumbColor,
@required this.disabledThumbColor, this.disabledThumbColor,
@required this.overlayColor, this.overlayColor,
@required this.valueIndicatorColor, this.valueIndicatorColor,
@required this.trackShape, this.trackShape,
@required this.tickMarkShape, this.tickMarkShape,
@required this.thumbShape, this.thumbShape,
@required this.overlayShape, this.overlayShape,
@required this.valueIndicatorShape, this.valueIndicatorShape,
@required this.showValueIndicator, this.showValueIndicator,
@required this.valueIndicatorTextStyle, this.valueIndicatorTextStyle,
}) : assert(trackHeight != null), });
assert(activeTrackColor != null),
assert(inactiveTrackColor != null),
assert(disabledActiveTrackColor != null),
assert(disabledInactiveTrackColor != null),
assert(activeTickMarkColor != null),
assert(inactiveTickMarkColor != null),
assert(disabledActiveTickMarkColor != null),
assert(disabledInactiveTickMarkColor != null),
assert(thumbColor != null),
assert(disabledThumbColor != null),
assert(overlayColor != null),
assert(valueIndicatorColor != null),
assert(trackShape != null),
assert(tickMarkShape != null),
assert(thumbShape != null),
assert(overlayShape != null),
assert(valueIndicatorShape != null),
assert(valueIndicatorTextStyle != null),
assert(showValueIndicator != null);
/// Generates a SliderThemeData from three main colors. /// Generates a SliderThemeData from three main colors.
/// ///
...@@ -283,15 +264,9 @@ class SliderThemeData extends Diagnosticable { ...@@ -283,15 +264,9 @@ class SliderThemeData extends Diagnosticable {
const int disabledInactiveTickMarkAlpha = 0x1f; // 12% opacity const int disabledInactiveTickMarkAlpha = 0x1f; // 12% opacity
const int thumbAlpha = 0xff; const int thumbAlpha = 0xff;
const int disabledThumbAlpha = 0x52; // 32% opacity const int disabledThumbAlpha = 0x52; // 32% opacity
const int overlayAlpha = 0x1f; // 12% opacity
const int valueIndicatorAlpha = 0xff; const int valueIndicatorAlpha = 0xff;
// TODO(gspencer): We don't really follow the spec here for overlays.
// The spec says to use 16% opacity for drawing over light material,
// and 32% for colored material, but we don't really have a way to
// know what the underlying color is, so there's no easy way to
// implement this. Choosing the "light" version for now.
const int overlayLightAlpha = 0x29; // 16% opacity
return SliderThemeData( return SliderThemeData(
trackHeight: 2.0, trackHeight: 2.0,
activeTrackColor: primaryColor.withAlpha(activeTrackAlpha), activeTrackColor: primaryColor.withAlpha(activeTrackAlpha),
...@@ -304,9 +279,9 @@ class SliderThemeData extends Diagnosticable { ...@@ -304,9 +279,9 @@ class SliderThemeData extends Diagnosticable {
disabledInactiveTickMarkColor: primaryColorDark.withAlpha(disabledInactiveTickMarkAlpha), disabledInactiveTickMarkColor: primaryColorDark.withAlpha(disabledInactiveTickMarkAlpha),
thumbColor: primaryColor.withAlpha(thumbAlpha), thumbColor: primaryColor.withAlpha(thumbAlpha),
disabledThumbColor: primaryColorDark.withAlpha(disabledThumbAlpha), disabledThumbColor: primaryColorDark.withAlpha(disabledThumbAlpha),
overlayColor: primaryColor.withAlpha(overlayLightAlpha), overlayColor: primaryColor.withAlpha(overlayAlpha),
valueIndicatorColor: primaryColor.withAlpha(valueIndicatorAlpha), valueIndicatorColor: primaryColor.withAlpha(valueIndicatorAlpha),
trackShape: const RectangularSliderTrackShape(), trackShape: const RoundedRectSliderTrackShape(),
tickMarkShape: const RoundSliderTickMarkShape(), tickMarkShape: const RoundSliderTickMarkShape(),
thumbShape: const RoundSliderThumbShape(), thumbShape: const RoundSliderThumbShape(),
overlayShape: const RoundSliderOverlayShape(), overlayShape: const RoundSliderOverlayShape(),
...@@ -927,15 +902,151 @@ class _EmptySliderComponentShape extends SliderComponentShape { ...@@ -927,15 +902,151 @@ class _EmptySliderComponentShape extends SliderComponentShape {
} }
} }
// The following shapes are the material defaults. /// Base track shape that provides an implementation of [getPreferredRect] for
/// default sizing.
///
/// See also:
///
/// * [RectangularSliderTrackShape], which is a track shape with sharp
/// rectangular edges
/// * [RoundedRectSliderTrackShape], which is a track shape with round
/// stadium-like edges.
///
/// The height is set from [SliderThemeData.trackHeight] and the width of the
/// parent box less the larger of the widths of [SliderThemeData.thumbShape] and
/// [SliderThemeData.overlayShape].
abstract class BaseSliderTrackShape {
/// Returns a rect that represents the track bounds that fits within the
/// [Slider].
///
/// The width is the width of the [Slider], but padded by the max
/// of the overlay and thumb radius. The height is defined by the
/// [SliderThemeData.trackHeight].
///
/// The [Rect] is centered both horizontally and vertically within the slider
/// bounds.
Rect getPreferredRect({
@required RenderBox parentBox,
Offset offset = Offset.zero,
SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
assert(parentBox != null);
final double thumbWidth = sliderTheme.thumbShape.getPreferredSize(isEnabled, isDiscrete).width;
final double overlayWidth = sliderTheme.overlayShape.getPreferredSize(isEnabled, isDiscrete).width;
final double trackHeight = sliderTheme.trackHeight;
assert(overlayWidth >= 0);
assert(trackHeight >= 0);
assert(parentBox.size.width >= overlayWidth);
assert(parentBox.size.height >= trackHeight);
final double trackLeft = offset.dx + overlayWidth / 2;
final double trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
final double trackWidth = parentBox.size.width - math.max(thumbWidth, overlayWidth);
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}
/// This is the default shape of a [Slider]'s track. /// This is the default shape of a [Slider]'s track.
/// ///
/// It paints a solid colored rectangle with rounded edges, vertically centered
/// in the [parentBox]. The track rectangle extends to the bounds of the
/// [parentBox], but is padded by the larger of [RoundSliderOverlayShape]'s
/// radius and [RoundSliderThumbShape]'s radius. The height is defined by the
/// [SliderThemeData.trackHeight]. The color is determined by the [Slider]'s
/// enabled state and the track segment's active state which are defined by:
/// [SliderThemeData.activeTrackColor],
/// [SliderThemeData.inactiveTrackColor],
/// [SliderThemeData.disabledActiveTrackColor],
/// [SliderThemeData.disabledInactiveTrackColor].
///
/// See also:
///
/// * [Slider], for the component that is meant to display this shape.
/// * [SliderThemeData], where an instance of this class is set to inform the
/// slider of the visual details of the its track.
/// * [SliderTrackShape], which is the base component for creating other
/// custom track shapes.
/// * [RectangularSliderTrackShape], for a similar track with sharp edges.
class RoundedRectSliderTrackShape extends SliderTrackShape with BaseSliderTrackShape {
/// Create a slider track that draws two rectangles with rounded outer edges.
const RoundedRectSliderTrackShape();
@override
void paint(
PaintingContext context,
Offset offset, {
@required RenderBox parentBox,
@required SliderThemeData sliderTheme,
@required Animation<double> enableAnimation,
@required TextDirection textDirection,
@required Offset thumbCenter,
bool isDiscrete = false,
bool isEnabled = false,
}) {
assert(context != null);
assert(offset != null);
assert(parentBox != null);
assert(sliderTheme != null);
assert(enableAnimation != null);
assert(textDirection != null);
assert(thumbCenter != null);
// If the slider track height is less than or equal to 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 leading: active and
// trailing: inactive.
final ColorTween activeTrackColorTween = ColorTween(begin: sliderTheme.disabledActiveTrackColor, end: sliderTheme.activeTrackColor);
final ColorTween inactiveTrackColorTween = ColorTween(begin: sliderTheme.disabledInactiveTrackColor, end: sliderTheme.inactiveTrackColor);
final Paint activePaint = Paint()..color = activeTrackColorTween.evaluate(enableAnimation);
final Paint inactivePaint = Paint()..color = inactiveTrackColorTween.evaluate(enableAnimation);
Paint leftTrackPaint;
Paint rightTrackPaint;
switch (textDirection) {
case TextDirection.ltr:
leftTrackPaint = activePaint;
rightTrackPaint = inactivePaint;
break;
case TextDirection.rtl:
leftTrackPaint = inactivePaint;
rightTrackPaint = activePaint;
break;
}
final Rect trackRect = getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
// The arc rects create a semi-circle with radius equal to track height.
final Rect leftTrackArcRect = Rect.fromLTWH(trackRect.left, trackRect.top, trackRect.height, trackRect.height);
context.canvas.drawArc(leftTrackArcRect, math.pi / 2, math.pi, false, leftTrackPaint);
final Rect rightTrackArcRect = Rect.fromLTWH(trackRect.right - trackRect.height / 2, trackRect.top, trackRect.height, trackRect.height);
context.canvas.drawArc(rightTrackArcRect, -math.pi / 2, math.pi, false, rightTrackPaint);
final Size thumbSize = sliderTheme.thumbShape.getPreferredSize(isEnabled, isDiscrete);
final Rect leftTrackSegment = Rect.fromLTRB(trackRect.left + trackRect.height / 2, trackRect.top, thumbCenter.dx - thumbSize.width / 2, trackRect.bottom);
context.canvas.drawRect(leftTrackSegment, leftTrackPaint);
final Rect rightTrackSegment = Rect.fromLTRB(thumbCenter.dx + thumbSize.width / 2, trackRect.top, trackRect.right, trackRect.bottom);
context.canvas.drawRect(rightTrackSegment, rightTrackPaint);
}
}
/// A [Slider] track that's a simple rectangle.
///
/// It paints a solid colored rectangle, vertically centered in the /// It paints a solid colored rectangle, vertically centered in the
/// [parentBox]. The track rectangle extends to the bounds of the [parentBox], /// [parentBox]. The track rectangle extends to the bounds of the [parentBox],
/// but is padded by the [RoundSliderOverlayShape] radius. The height is defined /// but is padded by the [RoundSliderOverlayShape] radius. The height is defined
/// by the [SliderThemeData.trackHeight]. The color is determined by the /// by the [SliderThemeData.trackHeight]. The color is determined by the
/// [Slider]'s enabled state and the track piece's active state which are /// [Slider]'s enabled state and the track segments's active state which are
/// defined by: /// defined by:
/// [SliderThemeData.activeTrackColor], /// [SliderThemeData.activeTrackColor],
/// [SliderThemeData.inactiveTrackColor], /// [SliderThemeData.inactiveTrackColor],
...@@ -944,11 +1055,12 @@ class _EmptySliderComponentShape extends SliderComponentShape { ...@@ -944,11 +1055,12 @@ class _EmptySliderComponentShape extends SliderComponentShape {
/// ///
/// See also: /// See also:
/// ///
/// * [Slider] for the component that this is meant to display this shape. /// * [Slider], for the component that is meant to display this shape.
/// * [SliderThemeData] where an instance of this class is set to inform the /// * [SliderThemeData], where an instance of this class is set to inform the
/// slider of the visual details of the its track. /// slider of the visual details of the its track.
/// * [SliderTrackShape] Base component for creating other custom track /// * [SliderTrackShape], which is the base component for creating other
/// shapes. /// custom track shapes.
/// * [RoundedRectSliderTrackShape], for a similar track with rounded edges.
class RectangularSliderTrackShape extends SliderTrackShape { class RectangularSliderTrackShape extends SliderTrackShape {
/// Create a slider track that draws 2 rectangles. /// Create a slider track that draws 2 rectangles.
const RectangularSliderTrackShape({ this.disabledThumbGapWidth = 2.0 }); const RectangularSliderTrackShape({ this.disabledThumbGapWidth = 2.0 });
...@@ -963,12 +1075,15 @@ class RectangularSliderTrackShape extends SliderTrackShape { ...@@ -963,12 +1075,15 @@ class RectangularSliderTrackShape extends SliderTrackShape {
@override @override
Rect getPreferredRect({ Rect getPreferredRect({
RenderBox parentBox, @required RenderBox parentBox,
Offset offset = Offset.zero, Offset offset = Offset.zero,
SliderThemeData sliderTheme, @required SliderThemeData sliderTheme,
bool isEnabled, bool isEnabled = false,
bool isDiscrete, bool isDiscrete = false,
}) { }) {
assert(parentBox != null);
assert(sliderTheme != null);
final double thumbWidth = sliderTheme.thumbShape.getPreferredSize(isEnabled, isDiscrete).width;
final double overlayWidth = sliderTheme.overlayShape.getPreferredSize(isEnabled, isDiscrete).width; final double overlayWidth = sliderTheme.overlayShape.getPreferredSize(isEnabled, isDiscrete).width;
final double trackHeight = sliderTheme.trackHeight; final double trackHeight = sliderTheme.trackHeight;
assert(overlayWidth >= 0); assert(overlayWidth >= 0);
...@@ -978,29 +1093,33 @@ class RectangularSliderTrackShape extends SliderTrackShape { ...@@ -978,29 +1093,33 @@ class RectangularSliderTrackShape extends SliderTrackShape {
final double trackLeft = offset.dx + overlayWidth / 2; final double trackLeft = offset.dx + overlayWidth / 2;
final double trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2; final double trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
// TODO(clocksmith): Although this works for a material, perhaps the default final double trackWidth = parentBox.size.width - math.max(thumbWidth, overlayWidth);
// rectangular track should be padded not just by the overlay, but by the
// max of the thumb and the overlay, in case there is no overlay.
final double trackWidth = parentBox.size.width - overlayWidth;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
} }
@override @override
void paint( void paint(
PaintingContext context, PaintingContext context,
Offset offset, { Offset offset, {
RenderBox parentBox, @required RenderBox parentBox,
SliderThemeData sliderTheme, @required SliderThemeData sliderTheme,
Animation<double> enableAnimation, @required Animation<double> enableAnimation,
TextDirection textDirection, @required TextDirection textDirection,
Offset thumbCenter, @required Offset thumbCenter,
bool isDiscrete, bool isDiscrete = false,
bool isEnabled, bool isEnabled = false,
}) { }) {
// If the slider track height is 0, then it makes no difference whether the assert(context != null);
// track is painted or not, therefore the painting can be a no-op. assert(offset != null);
if (sliderTheme.trackHeight == 0) { assert(parentBox != null);
assert(sliderTheme != null);
assert(enableAnimation != null);
assert(textDirection != null);
assert(thumbCenter != null);
// If the slider track height is less than or equal to 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; return;
} }
...@@ -1023,28 +1142,18 @@ class RectangularSliderTrackShape extends SliderTrackShape { ...@@ -1023,28 +1142,18 @@ class RectangularSliderTrackShape extends SliderTrackShape {
break; break;
} }
// Used to create a gap around the thumb iff the slider is disabled.
// If the slider is enabled, the track can be drawn beneath the thumb
// without a gap. But when the slider is disabled, the track is shortened
// and this gap helps determine how much shorter it should be.
// TODO(clocksmith): The new Material spec has a gray circle in place of this gap.
double horizontalAdjustment = 0.0;
if (!isEnabled) {
final double disabledThumbRadius = sliderTheme.thumbShape.getPreferredSize(false, isDiscrete).width / 2.0;
final double gap = disabledThumbGapWidth * (1.0 - enableAnimation.value);
horizontalAdjustment = disabledThumbRadius + gap;
}
final Rect trackRect = getPreferredRect( final Rect trackRect = getPreferredRect(
parentBox: parentBox, parentBox: parentBox,
offset: offset, offset: offset,
sliderTheme: sliderTheme, sliderTheme: sliderTheme,
isEnabled: isEnabled, isEnabled: isEnabled,
isDiscrete: isDiscrete, isDiscrete: isDiscrete,
); );
final Rect leftTrackSegment = Rect.fromLTRB(trackRect.left, trackRect.top, thumbCenter.dx - horizontalAdjustment, trackRect.bottom);
final Size thumbSize = sliderTheme.thumbShape.getPreferredSize(isEnabled, isDiscrete);
final Rect leftTrackSegment = Rect.fromLTRB(trackRect.left + trackRect.height / 2, trackRect.top, thumbCenter.dx - thumbSize.width / 2, trackRect.bottom);
context.canvas.drawRect(leftTrackSegment, leftTrackPaint); context.canvas.drawRect(leftTrackSegment, leftTrackPaint);
final Rect rightTrackSegment = Rect.fromLTRB(thumbCenter.dx + horizontalAdjustment, trackRect.top, trackRect.right, trackRect.bottom); final Rect rightTrackSegment = Rect.fromLTRB(thumbCenter.dx + thumbSize.width / 2, trackRect.top, trackRect.right, trackRect.bottom);
context.canvas.drawRect(rightTrackSegment, rightTrackPaint); context.canvas.drawRect(rightTrackSegment, rightTrackPaint);
} }
} }
...@@ -1090,13 +1199,20 @@ class RoundSliderTickMarkShape extends SliderTickMarkShape { ...@@ -1090,13 +1199,20 @@ class RoundSliderTickMarkShape extends SliderTickMarkShape {
void paint( void paint(
PaintingContext context, PaintingContext context,
Offset center, { Offset center, {
RenderBox parentBox, @required RenderBox parentBox,
SliderThemeData sliderTheme, @required SliderThemeData sliderTheme,
Animation<double> enableAnimation, @required Animation<double> enableAnimation,
TextDirection textDirection, @required TextDirection textDirection,
Offset thumbCenter, @required Offset thumbCenter,
bool isEnabled, bool isEnabled = false,
}) { }) {
assert(context != null);
assert(center != null);
assert(parentBox != null);
assert(sliderTheme != null);
assert(enableAnimation != null);
assert(textDirection != null);
assert(thumbCenter != null);
// The paint color of the tick mark depends on its position relative // The paint color of the tick mark depends on its position relative
// to the thumb and the text direction. // to the thumb and the text direction.
Color begin; Color begin;
...@@ -1133,26 +1249,22 @@ class RoundSliderTickMarkShape extends SliderTickMarkShape { ...@@ -1133,26 +1249,22 @@ class RoundSliderTickMarkShape extends SliderTickMarkShape {
/// sliders in a widget subtree. /// sliders in a widget subtree.
class RoundSliderThumbShape extends SliderComponentShape { class RoundSliderThumbShape extends SliderComponentShape {
/// Create a slider thumb that draws a circle. /// Create a slider thumb that draws a circle.
// TODO(clocksmith): This needs to be changed to 10 according to spec.
const RoundSliderThumbShape({ const RoundSliderThumbShape({
this.enabledThumbRadius = 6.0, this.enabledThumbRadius = 10.0,
this.disabledThumbRadius, this.disabledThumbRadius,
}); });
/// The preferred radius of the round thumb shape when the slider is enabled. /// The preferred radius of the round thumb shape when the slider is enabled.
/// ///
/// If it is not provided, then the material default is used. /// If it is not provided, then the material default of 10 is used.
final double enabledThumbRadius; final double enabledThumbRadius;
/// The preferred radius of the round thumb shape when the slider is disabled. /// The preferred radius of the round thumb shape when the slider is disabled.
/// ///
/// If no disabledRadius is provided, then it is is derived from the enabled /// If no disabledRadius is provided, then it is equal to the
/// thumb radius and has the same ratio of enabled size to disabled size as /// [enabledThumbRadius]
/// the Material spec. The default resolves to 4, which is 2 / 3 of the
/// default enabled thumb.
final double disabledThumbRadius; final double disabledThumbRadius;
// TODO(clocksmith): This needs to be updated once the thumb size is updated to the Material spec. double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius;
double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius * 2 / 3;
@override @override
Size getPreferredSize(bool isEnabled, bool isDiscrete) { Size getPreferredSize(bool isEnabled, bool isDiscrete) {
...@@ -1163,15 +1275,24 @@ class RoundSliderThumbShape extends SliderComponentShape { ...@@ -1163,15 +1275,24 @@ class RoundSliderThumbShape extends SliderComponentShape {
void paint( void paint(
PaintingContext context, PaintingContext context,
Offset center, { Offset center, {
Animation<double> activationAnimation, @required Animation<double> activationAnimation,
Animation<double> enableAnimation, @required Animation<double> enableAnimation,
bool isDiscrete, bool isDiscrete = false,
TextPainter labelPainter, @required TextPainter labelPainter,
RenderBox parentBox, @required RenderBox parentBox,
SliderThemeData sliderTheme, @required SliderThemeData sliderTheme,
TextDirection textDirection, @required TextDirection textDirection,
double value, @required double value,
}) { }) {
assert(context != null);
assert(center != null);
assert(activationAnimation != null);
assert(enableAnimation != null);
assert(labelPainter != null);
assert(parentBox != null);
assert(sliderTheme != null);
assert(textDirection != null);
assert(value != null);
final Canvas canvas = context.canvas; final Canvas canvas = context.canvas;
final Tween<double> radiusTween = Tween<double>( final Tween<double> radiusTween = Tween<double>(
begin: _disabledThumbRadius, begin: _disabledThumbRadius,
...@@ -1206,8 +1327,7 @@ class RoundSliderThumbShape extends SliderComponentShape { ...@@ -1206,8 +1327,7 @@ class RoundSliderThumbShape extends SliderComponentShape {
/// sliders in a widget subtree. /// sliders in a widget subtree.
class RoundSliderOverlayShape extends SliderComponentShape { class RoundSliderOverlayShape extends SliderComponentShape {
/// Create a slider thumb overlay that draws a circle. /// Create a slider thumb overlay that draws a circle.
// TODO(clocksmith): This needs to be changed to 24 according to spec. const RoundSliderOverlayShape({ this.overlayRadius = 24.0 });
const RoundSliderOverlayShape({ this.overlayRadius = 16.0 });
/// The preferred radius of the round thumb shape when enabled. /// The preferred radius of the round thumb shape when enabled.
/// ///
...@@ -1223,26 +1343,30 @@ class RoundSliderOverlayShape extends SliderComponentShape { ...@@ -1223,26 +1343,30 @@ class RoundSliderOverlayShape extends SliderComponentShape {
void paint( void paint(
PaintingContext context, PaintingContext context,
Offset center, { Offset center, {
Animation<double> activationAnimation, @required Animation<double> activationAnimation,
Animation<double> enableAnimation, @required Animation<double> enableAnimation,
bool isDiscrete, bool isDiscrete = false,
TextPainter labelPainter, @required TextPainter labelPainter,
RenderBox parentBox, @required RenderBox parentBox,
SliderThemeData sliderTheme, @required SliderThemeData sliderTheme,
TextDirection textDirection, @required TextDirection textDirection,
double value, @required double value,
}) { }) {
assert(context != null);
assert(center != null);
assert(activationAnimation != null);
assert(enableAnimation != null);
assert(labelPainter != null);
assert(parentBox != null);
assert(sliderTheme != null);
assert(textDirection != null);
assert(value != null);
final Canvas canvas = context.canvas; final Canvas canvas = context.canvas;
final Tween<double> radiusTween = Tween<double>( final Tween<double> radiusTween = Tween<double>(
begin: 0.0, begin: 0.0,
end: overlayRadius, end: overlayRadius,
); );
// TODO(gspencer): We don't really follow the spec here for overlays.
// The spec says to use 16% opacity for drawing over light material,
// and 32% for colored material, but we don't really have a way to
// know what the underlying color is, so there's no easy way to
// implement this. Choosing the "light" version for now.
canvas.drawCircle( canvas.drawCircle(
center, center,
radiusTween.evaluate(activationAnimation), radiusTween.evaluate(activationAnimation),
...@@ -1324,7 +1448,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ...@@ -1324,7 +1448,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
final Path path = Path(); final Path path = Path();
final Offset bottomKnobStart = Offset( final Offset bottomKnobStart = Offset(
_bottomLobeRadius * math.cos(_bottomLobeStartAngle), _bottomLobeRadius * math.cos(_bottomLobeStartAngle),
_bottomLobeRadius * math.sin(_bottomLobeStartAngle), _bottomLobeRadius * math.sin(_bottomLobeStartAngle) - 2,
); );
final Offset bottomNeckRightCenter = bottomKnobStart + final Offset bottomNeckRightCenter = bottomKnobStart +
Offset( Offset(
...@@ -1474,7 +1598,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ...@@ -1474,7 +1598,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
// The distance between the end of the bottom neck arc and the beginning of // The distance between the end of the bottom neck arc and the beginning of
// the top neck arc. We use this to shrink/expand it based on the scale // the top neck arc. We use this to shrink/expand it based on the scale
// factor of the value indicator. // factor of the value indicator.
final double neckStretchBaseline = bottomLobeEnd.dy - math.max(neckLeftCenter.dy, neckRightCenter.dy); final double neckStretchBaseline = math.max(0.0, bottomLobeEnd.dy - math.max(neckLeftCenter.dy, neckRightCenter.dy));
final double t = math.pow(inverseTextScale, 3.0); final double t = math.pow(inverseTextScale, 3.0);
final double stretch = (neckStretchBaseline * t).clamp(0.0, 10.0 * neckStretchBaseline); final double stretch = (neckStretchBaseline * t).clamp(0.0, 10.0 * neckStretchBaseline);
final Offset neckStretch = Offset(0.0, neckStretchBaseline - stretch); final Offset neckStretch = Offset(0.0, neckStretchBaseline - stretch);
...@@ -1540,15 +1664,24 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ...@@ -1540,15 +1664,24 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
void paint( void paint(
PaintingContext context, PaintingContext context,
Offset center, { Offset center, {
Animation<double> activationAnimation, @required Animation<double> activationAnimation,
Animation<double> enableAnimation, @required Animation<double> enableAnimation,
bool isDiscrete, bool isDiscrete = false,
TextPainter labelPainter, @required TextPainter labelPainter,
RenderBox parentBox, @required RenderBox parentBox,
SliderThemeData sliderTheme, @required SliderThemeData sliderTheme,
TextDirection textDirection, @required TextDirection textDirection,
double value, @required double value,
}) { }) {
assert(context != null);
assert(center != null);
assert(activationAnimation != null);
assert(enableAnimation != null);
assert(labelPainter != null);
assert(parentBox != null);
assert(sliderTheme != null);
assert(textDirection != null);
assert(value != null);
final ColorTween enableColor = ColorTween( final ColorTween enableColor = ColorTween(
begin: sliderTheme.disabledThumbColor, begin: sliderTheme.disabledThumbColor,
end: sliderTheme.valueIndicatorColor, end: sliderTheme.valueIndicatorColor,
......
...@@ -243,12 +243,7 @@ class ThemeData extends Diagnosticable { ...@@ -243,12 +243,7 @@ class ThemeData extends Diagnosticable {
highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor; highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor;
splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor; splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor;
sliderTheme ??= SliderThemeData.fromPrimaryColors( sliderTheme ??= const SliderThemeData();
primaryColor: primaryColor,
primaryColorLight: primaryColorLight,
primaryColorDark: primaryColorDark,
valueIndicatorTextStyle: accentTextTheme.body2,
);
tabBarTheme ??= const TabBarTheme(); tabBarTheme ??= const TabBarTheme();
appBarTheme ??= const AppBarTheme(); appBarTheme ??= const AppBarTheme();
bottomAppBarTheme ??= const BottomAppBarTheme(); bottomAppBarTheme ??= const BottomAppBarTheme();
......
...@@ -298,8 +298,8 @@ void main() { ...@@ -298,8 +298,8 @@ void main() {
); );
final List<Offset> expectedLog = <Offset>[ final List<Offset> expectedLog = <Offset>[
const Offset(16.0, 300.0), const Offset(24.0, 300.0),
const Offset(16.0, 300.0), const Offset(24.0, 300.0),
const Offset(400.0, 300.0), const Offset(400.0, 300.0),
]; ];
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(sliderKey))); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(sliderKey)));
...@@ -313,20 +313,20 @@ void main() { ...@@ -313,20 +313,20 @@ void main() {
await tester.pump(const Duration(milliseconds: 10)); await tester.pump(const Duration(milliseconds: 10));
expect(value, equals(0.0)); expect(value, equals(0.0));
expect(log.length, 5); expect(log.length, 5);
expect(log.last.dx, closeTo(386.3, 0.1)); expect(log.last.dx, closeTo(386.6, 0.1));
// With no more gesture or value changes, the thumb position should still // With no more gesture or value changes, the thumb position should still
// be redrawn in the animated position. // be redrawn in the animated position.
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 10)); await tester.pump(const Duration(milliseconds: 10));
expect(value, equals(0.0)); expect(value, equals(0.0));
expect(log.length, 7); expect(log.length, 7);
expect(log.last.dx, closeTo(343.3, 0.1)); expect(log.last.dx, closeTo(344.5, 0.1));
// Final position. // Final position.
await tester.pump(const Duration(milliseconds: 80)); await tester.pump(const Duration(milliseconds: 80));
expectedLog.add(const Offset(16.0, 300.0)); expectedLog.add(const Offset(24.0, 300.0));
expect(value, equals(0.0)); expect(value, equals(0.0));
expect(log.length, 8); expect(log.length, 8);
expect(log.last.dx, closeTo(16.0, 0.1)); expect(log.last.dx, closeTo(24.0, 0.1));
await gesture.up(); await gesture.up();
}); });
...@@ -409,8 +409,8 @@ void main() { ...@@ -409,8 +409,8 @@ void main() {
); );
final List<Offset> expectedLog = <Offset>[ final List<Offset> expectedLog = <Offset>[
const Offset(16.0, 300.0), const Offset(24.0, 300.0),
const Offset(16.0, 300.0), const Offset(24.0, 300.0),
const Offset(400.0, 300.0), const Offset(400.0, 300.0),
]; ];
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(sliderKey))); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(sliderKey)));
...@@ -424,20 +424,20 @@ void main() { ...@@ -424,20 +424,20 @@ void main() {
await tester.pump(const Duration(milliseconds: 10)); await tester.pump(const Duration(milliseconds: 10));
expect(value, equals(0.0)); expect(value, equals(0.0));
expect(log.length, 5); expect(log.length, 5);
expect(log.last.dx, closeTo(386.3, 0.1)); expect(log.last.dx, closeTo(386.6, 0.1));
// With no more gesture or value changes, the thumb position should still // With no more gesture or value changes, the thumb position should still
// be redrawn in the animated position. // be redrawn in the animated position.
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 10)); await tester.pump(const Duration(milliseconds: 10));
expect(value, equals(0.0)); expect(value, equals(0.0));
expect(log.length, 7); expect(log.length, 7);
expect(log.last.dx, closeTo(343.3, 0.1)); expect(log.last.dx, closeTo(344.5, 0.1));
// Final position. // Final position.
await tester.pump(const Duration(milliseconds: 80)); await tester.pump(const Duration(milliseconds: 80));
expectedLog.add(const Offset(16.0, 300.0)); expectedLog.add(const Offset(24.0, 300.0));
expect(value, equals(0.0)); expect(value, equals(0.0));
expect(log.length, 8); expect(log.length, 8);
expect(log.last.dx, closeTo(16.0, 0.1)); expect(log.last.dx, closeTo(24.0, 0.1));
await gesture.up(); await gesture.up();
}); });
...@@ -546,6 +546,20 @@ void main() { ...@@ -546,6 +546,20 @@ void main() {
final ThemeData theme = ThemeData( final ThemeData theme = ThemeData(
platform: TargetPlatform.android, platform: TargetPlatform.android,
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
sliderTheme: const SliderThemeData(
disabledThumbColor: Color(0xff000001),
disabledActiveTickMarkColor: Color(0xff000002),
disabledActiveTrackColor: Color(0xff000003),
disabledInactiveTickMarkColor: Color(0xff000004),
disabledInactiveTrackColor: Color(0xff000005),
activeTrackColor: Color(0xff000006),
activeTickMarkColor: Color(0xff000007),
inactiveTrackColor: Color(0xff000008),
inactiveTickMarkColor: Color(0xff000009),
overlayColor: Color(0xff000010),
thumbColor: Color(0xff000011),
valueIndicatorColor: Color(0xff000012),
)
); );
final SliderThemeData sliderTheme = theme.sliderTheme; final SliderThemeData sliderTheme = theme.sliderTheme;
double value = 0.45; double value = 0.45;
...@@ -724,7 +738,7 @@ void main() { ...@@ -724,7 +738,7 @@ void main() {
paints paints
..rect(color: customColor1) // active track ..rect(color: customColor1) // active track
..rect(color: customColor2) // inactive track ..rect(color: customColor2) // inactive track
..circle(color: customColor1.withAlpha(0x29)) // overlay ..circle(color: customColor1.withOpacity(0.12)) // overlay
..circle(color: customColor2) // 1st tick mark ..circle(color: customColor2) // 1st tick mark
..circle(color: customColor2) // 2nd tick mark ..circle(color: customColor2) // 2nd tick mark
..circle(color: customColor2) // 3rd tick mark ..circle(color: customColor2) // 3rd tick mark
...@@ -858,7 +872,7 @@ void main() { ...@@ -858,7 +872,7 @@ void main() {
), ),
), ),
)); ));
expect(tester.renderObject<RenderBox>(find.byType(Slider)).size, const Size(144.0 + 2.0 * 16.0, 600.0)); expect(tester.renderObject<RenderBox>(find.byType(Slider)).size, const Size(144.0 + 2.0 * 24.0, 600.0));
await tester.pumpWidget(Directionality( await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -878,7 +892,7 @@ void main() { ...@@ -878,7 +892,7 @@ void main() {
), ),
), ),
)); ));
expect(tester.renderObject<RenderBox>(find.byType(Slider)).size, const Size(144.0 + 2.0 * 16.0, 32.0)); expect(tester.renderObject<RenderBox>(find.byType(Slider)).size, const Size(144.0 + 2.0 * 24.0, 48.0));
}); });
testWidgets('Slider respects textScaleFactor', (WidgetTester tester) async { testWidgets('Slider respects textScaleFactor', (WidgetTester tester) async {
...@@ -1077,12 +1091,12 @@ void main() { ...@@ -1077,12 +1091,12 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..circle(x: 17.0, y: 16.0, radius: 1.0) ..circle(x: 25.0, y: 24.0, radius: 1.0)
..circle(x: 208.5, y: 16.0, radius: 1.0) ..circle(x: 212.5, y: 24.0, radius: 1.0)
..circle(x: 400.0, y: 16.0, radius: 1.0) ..circle(x: 400.0, y: 24.0, radius: 1.0)
..circle(x: 591.5, y: 16.0, radius: 1.0) ..circle(x: 587.5, y: 24.0, radius: 1.0)
..circle(x: 783.0, y: 16.0, radius: 1.0) ..circle(x: 775.0, y: 24.0, radius: 1.0)
..circle(x: 16.0, y: 16.0, radius: 6.0), ..circle(x: 24.0, y: 24.0, radius: 10.0),
); );
gesture = await tester.startGesture(center); gesture = await tester.startGesture(center);
...@@ -1093,13 +1107,13 @@ void main() { ...@@ -1093,13 +1107,13 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..circle(x: 105.0625, y: 16.0, radius: 3.791776657104492) ..circle(x: 111.20703125, y: 24.0, radius: 5.687664985656738)
..circle(x: 17.0, y: 16.0, radius: 1.0) ..circle(x: 25.0, y: 24.0, radius: 1.0)
..circle(x: 208.5, y: 16.0, radius: 1.0) ..circle(x: 212.5, y: 24.0, radius: 1.0)
..circle(x: 400.0, y: 16.0, radius: 1.0) ..circle(x: 400.0, y: 24.0, radius: 1.0)
..circle(x: 591.5, y: 16.0, radius: 1.0) ..circle(x: 587.5, y: 24.0, radius: 1.0)
..circle(x: 783.0, y: 16.0, radius: 1.0) ..circle(x: 775.0, y: 24.0, radius: 1.0)
..circle(x: 105.0625, y: 16.0, radius: 6.0), ..circle(x: 111.20703125, y: 24.0, radius: 10.0),
); );
// Reparenting in the middle of an animation should do nothing. // Reparenting in the middle of an animation should do nothing.
...@@ -1113,13 +1127,13 @@ void main() { ...@@ -1113,13 +1127,13 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..circle(x: 185.5457763671875, y: 16.0, radius: 8.0) ..circle(x: 190.0135726928711, y: 24.0, radius: 12.0)
..circle(x: 17.0, y: 16.0, radius: 1.0) ..circle(x: 25.0, y: 24.0, radius: 1.0)
..circle(x: 208.5, y: 16.0, radius: 1.0) ..circle(x: 212.5, y: 24.0, radius: 1.0)
..circle(x: 400.0, y: 16.0, radius: 1.0) ..circle(x: 400.0, y: 24.0, radius: 1.0)
..circle(x: 591.5, y: 16.0, radius: 1.0) ..circle(x: 587.5, y: 24.0, radius: 1.0)
..circle(x: 783.0, y: 16.0, radius: 1.0) ..circle(x: 775.0, y: 24.0, radius: 1.0)
..circle(x: 185.5457763671875, y: 16.0, radius: 6.0), ..circle(x: 190.0135726928711, y: 24.0, radius: 10.0),
); );
// Wait for animations to finish. // Wait for animations to finish.
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -1127,13 +1141,13 @@ void main() { ...@@ -1127,13 +1141,13 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..circle(x: 400.0, y: 16.0, radius: 16.0) ..circle(x: 400.0, y: 24.0, radius: 24.0)
..circle(x: 17.0, y: 16.0, radius: 1.0) ..circle(x: 25.0, y: 24.0, radius: 1.0)
..circle(x: 208.5, y: 16.0, radius: 1.0) ..circle(x: 212.5, y: 24.0, radius: 1.0)
..circle(x: 400.0, y: 16.0, radius: 1.0) ..circle(x: 400.0, y: 24.0, radius: 1.0)
..circle(x: 591.5, y: 16.0, radius: 1.0) ..circle(x: 587.5, y: 24.0, radius: 1.0)
..circle(x: 783.0, y: 16.0, radius: 1.0) ..circle(x: 775.0, y: 24.0, radius: 1.0)
..circle(x: 400.0, y: 16.0, radius: 6.0), ..circle(x: 400.0, y: 24.0, radius: 10.0),
); );
await gesture.up(); await gesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -1141,12 +1155,12 @@ void main() { ...@@ -1141,12 +1155,12 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..circle(x: 17.0, y: 16.0, radius: 1.0) ..circle(x: 25.0, y: 24.0, radius: 1.0)
..circle(x: 208.5, y: 16.0, radius: 1.0) ..circle(x: 212.5, y: 24.0, radius: 1.0)
..circle(x: 400.0, y: 16.0, radius: 1.0) ..circle(x: 400.0, y: 24.0, radius: 1.0)
..circle(x: 591.5, y: 16.0, radius: 1.0) ..circle(x: 587.5, y: 24.0, radius: 1.0)
..circle(x: 783.0, y: 16.0, radius: 1.0) ..circle(x: 775.0, y: 24.0, radius: 1.0)
..circle(x: 400.0, y: 16.0, radius: 6.0), ..circle(x: 400.0, y: 24.0, radius: 10.0),
); );
} }
......
...@@ -12,17 +12,6 @@ import 'package:flutter/painting.dart'; ...@@ -12,17 +12,6 @@ import 'package:flutter/painting.dart';
import '../rendering/mock_canvas.dart'; import '../rendering/mock_canvas.dart';
void main() { void main() {
testWidgets('Slider theme is built by ThemeData', (WidgetTester tester) async {
final ThemeData theme = ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.red,
);
final SliderThemeData sliderTheme = theme.sliderTheme;
expect(sliderTheme.activeTrackColor.value, equals(Colors.red.value));
expect(sliderTheme.inactiveTrackColor.value, equals(Colors.red.withAlpha(0x3d).value));
});
testWidgets('Slider uses ThemeData slider theme if present', (WidgetTester tester) async { testWidgets('Slider uses ThemeData slider theme if present', (WidgetTester tester) async {
final ThemeData theme = ThemeData( final ThemeData theme = ThemeData(
platform: TargetPlatform.android, platform: TargetPlatform.android,
...@@ -63,20 +52,6 @@ void main() { ...@@ -63,20 +52,6 @@ void main() {
); );
}); });
testWidgets('SliderThemeData assigns the correct default shapes', (WidgetTester tester) async {
final SliderThemeData sliderTheme = ThemeData().sliderTheme;
expect(sliderTheme.trackShape, equals(isInstanceOf<RectangularSliderTrackShape>()));
expect(sliderTheme.tickMarkShape, equals(isInstanceOf<RoundSliderTickMarkShape>()));
expect(sliderTheme.thumbShape, equals(isInstanceOf<RoundSliderThumbShape>()));
expect(sliderTheme.valueIndicatorShape, equals(isInstanceOf<PaddleSliderValueIndicatorShape>()));
expect(sliderTheme.overlayShape, equals(isInstanceOf<RoundSliderOverlayShape>()));
});
testWidgets('SliderThemeData assigns the correct default flags', (WidgetTester tester) async {
final SliderThemeData sliderTheme = ThemeData().sliderTheme;
expect(sliderTheme.showValueIndicator, equals(ShowValueIndicator.onlyForDiscrete));
});
testWidgets('SliderThemeData generates correct opacities for fromPrimaryColors', (WidgetTester tester) async { testWidgets('SliderThemeData generates correct opacities for fromPrimaryColors', (WidgetTester tester) async {
const Color customColor1 = Color(0xcafefeed); const Color customColor1 = Color(0xcafefeed);
const Color customColor2 = Color(0xdeadbeef); const Color customColor2 = Color(0xdeadbeef);
...@@ -100,7 +75,7 @@ void main() { ...@@ -100,7 +75,7 @@ void main() {
expect(sliderTheme.disabledInactiveTickMarkColor, equals(customColor2.withAlpha(0x1f))); expect(sliderTheme.disabledInactiveTickMarkColor, equals(customColor2.withAlpha(0x1f)));
expect(sliderTheme.thumbColor, equals(customColor1.withAlpha(0xff))); expect(sliderTheme.thumbColor, equals(customColor1.withAlpha(0xff)));
expect(sliderTheme.disabledThumbColor, equals(customColor2.withAlpha(0x52))); expect(sliderTheme.disabledThumbColor, equals(customColor2.withAlpha(0x52)));
expect(sliderTheme.overlayColor, equals(customColor1.withAlpha(0x29))); expect(sliderTheme.overlayColor, equals(customColor1.withAlpha(0x1f)));
expect(sliderTheme.valueIndicatorColor, equals(customColor1.withAlpha(0xff))); expect(sliderTheme.valueIndicatorColor, equals(customColor1.withAlpha(0xff)));
expect(sliderTheme.valueIndicatorTextStyle.color, equals(customColor4)); expect(sliderTheme.valueIndicatorTextStyle.color, equals(customColor4));
}); });
...@@ -132,7 +107,7 @@ void main() { ...@@ -132,7 +107,7 @@ void main() {
expect(lerp.disabledInactiveTickMarkColor, equals(middleGrey.withAlpha(0x1f))); expect(lerp.disabledInactiveTickMarkColor, equals(middleGrey.withAlpha(0x1f)));
expect(lerp.thumbColor, equals(middleGrey.withAlpha(0xff))); expect(lerp.thumbColor, equals(middleGrey.withAlpha(0xff)));
expect(lerp.disabledThumbColor, equals(middleGrey.withAlpha(0x52))); expect(lerp.disabledThumbColor, equals(middleGrey.withAlpha(0x52)));
expect(lerp.overlayColor, equals(middleGrey.withAlpha(0x29))); expect(lerp.overlayColor, equals(middleGrey.withAlpha(0x1f)));
expect(lerp.valueIndicatorColor, equals(middleGrey.withAlpha(0xff))); expect(lerp.valueIndicatorColor, equals(middleGrey.withAlpha(0xff)));
expect(lerp.valueIndicatorTextStyle.color, equals(middleGrey.withAlpha(0xff))); expect(lerp.valueIndicatorTextStyle.color, equals(middleGrey.withAlpha(0xff)));
}); });
...@@ -152,8 +127,8 @@ void main() { ...@@ -152,8 +127,8 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..rect(rect: Rect.fromLTRB(16.0, 299.0, 208.0, 301.0), color: sliderTheme.activeTrackColor) ..rect(rect: Rect.fromLTRB(25.0, 299.0, 202.0, 301.0), color: sliderTheme.activeTrackColor)
..rect(rect: Rect.fromLTRB(208.0, 299.0, 784.0, 301.0), color: sliderTheme.inactiveTrackColor), ..rect(rect: Rect.fromLTRB(222.0, 299.0, 776.0, 301.0), color: sliderTheme.inactiveTrackColor),
); );
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
...@@ -168,8 +143,8 @@ void main() { ...@@ -168,8 +143,8 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..rect(rect: Rect.fromLTRB(16.0, 299.0, 202.0, 301.0), color: sliderTheme.disabledActiveTrackColor) ..rect(rect: Rect.fromLTRB(25.0, 299.0, 202.0, 301.0), color: sliderTheme.disabledActiveTrackColor)
..rect(rect: Rect.fromLTRB(214.0, 299.0, 784.0, 301.0), color: sliderTheme.disabledInactiveTrackColor), ..rect(rect: Rect.fromLTRB(222.0, 299.0, 776.0, 301.0), color: sliderTheme.disabledInactiveTrackColor),
); );
}); });
...@@ -189,9 +164,9 @@ void main() { ...@@ -189,9 +164,9 @@ void main() {
paints paints
..circle( ..circle(
color: sliderTheme.thumbColor, color: sliderTheme.thumbColor,
x: 208.0, x: 212.0,
y: 300.0, y: 300.0,
radius: 6.0, radius: 10.0,
), ),
); );
...@@ -206,15 +181,15 @@ void main() { ...@@ -206,15 +181,15 @@ void main() {
paints paints
..circle( ..circle(
color: sliderTheme.overlayColor, color: sliderTheme.overlayColor,
x: 208.0, x: 212.0,
y: 300.0, y: 300.0,
radius: 16.0, radius: 24.0,
) )
..circle( ..circle(
color: sliderTheme.thumbColor, color: sliderTheme.thumbColor,
x: 208.0, x: 212.0,
y: 300.0, y: 300.0,
radius: 6.0, radius: 10.0,
), ),
); );
...@@ -227,9 +202,9 @@ void main() { ...@@ -227,9 +202,9 @@ void main() {
paints paints
..circle( ..circle(
color: sliderTheme.thumbColor, color: sliderTheme.thumbColor,
x: 208.0, x: 212.0,
y: 300.0, y: 300.0,
radius: 6.0, radius: 10.0,
), ),
); );
}); });
...@@ -244,12 +219,12 @@ void main() { ...@@ -244,12 +219,12 @@ void main() {
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45));
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider)); final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
expect(sliderBox, paints..circle(color: sliderTheme.thumbColor, radius: 6.0)); expect(sliderBox, paints..circle(color: sliderTheme.thumbColor, radius: 10.0));
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, enabled: false)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, enabled: false));
await tester.pumpAndSettle(); // wait for disable animation await tester.pumpAndSettle(); // wait for disable animation
expect(sliderBox, paints..circle(color: sliderTheme.disabledThumbColor, radius: 4.0)); expect(sliderBox, paints..circle(color: sliderTheme.disabledThumbColor, radius: 10.0));
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, divisions: 3)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, divisions: 3));
await tester.pumpAndSettle(); // wait for enable animation await tester.pumpAndSettle(); // wait for enable animation
...@@ -261,7 +236,7 @@ void main() { ...@@ -261,7 +236,7 @@ void main() {
..circle(color: sliderTheme.activeTickMarkColor) ..circle(color: sliderTheme.activeTickMarkColor)
..circle(color: sliderTheme.inactiveTickMarkColor) ..circle(color: sliderTheme.inactiveTickMarkColor)
..circle(color: sliderTheme.inactiveTickMarkColor) ..circle(color: sliderTheme.inactiveTickMarkColor)
..circle(color: sliderTheme.thumbColor, radius: 6.0), ..circle(color: sliderTheme.thumbColor, radius: 10.0),
); );
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, divisions: 3, enabled: false)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, divisions: 3, enabled: false));
...@@ -274,7 +249,7 @@ void main() { ...@@ -274,7 +249,7 @@ void main() {
..circle(color: sliderTheme.disabledInactiveTickMarkColor) ..circle(color: sliderTheme.disabledInactiveTickMarkColor)
..circle(color: sliderTheme.disabledInactiveTickMarkColor) ..circle(color: sliderTheme.disabledInactiveTickMarkColor)
..circle(color: sliderTheme.disabledInactiveTickMarkColor) ..circle(color: sliderTheme.disabledInactiveTickMarkColor)
..circle(color: sliderTheme.disabledThumbColor, radius: 4.0), ..circle(color: sliderTheme.disabledThumbColor, radius: 10.0),
); );
}); });
...@@ -368,10 +343,10 @@ void main() { ...@@ -368,10 +343,10 @@ void main() {
color: sliderTheme.valueIndicatorColor, color: sliderTheme.valueIndicatorColor,
includes: <Offset>[ includes: <Offset>[
const Offset(0.0, -40.0), const Offset(0.0, -40.0),
const Offset(98.0, -40.0), const Offset(92.0, -40.0),
const Offset(-16.0, -40.0), const Offset(-16.0, -40.0),
], ],
excludes: <Offset>[const Offset(98.1, -40.0), const Offset(-16.1, -40.0)], excludes: <Offset>[const Offset(98.1, -40.0), const Offset(-20.1, -40.0)],
), ),
); );
await gesture.up(); await gesture.up();
...@@ -390,9 +365,9 @@ void main() { ...@@ -390,9 +365,9 @@ void main() {
includes: <Offset>[ includes: <Offset>[
const Offset(0.0, -40.0), const Offset(0.0, -40.0),
const Offset(16.0, -40.0), const Offset(16.0, -40.0),
const Offset(-98.0, -40.0), const Offset(-92.0, -40.0),
], ],
excludes: <Offset>[const Offset(16.1, -40.0), const Offset(-98.1, -40.0)], excludes: <Offset>[const Offset(20.1, -40.0), const Offset(-98.1, -40.0)],
), ),
); );
await gesture.up(); await gesture.up();
...@@ -410,14 +385,14 @@ void main() { ...@@ -410,14 +385,14 @@ void main() {
color: sliderTheme.valueIndicatorColor, color: sliderTheme.valueIndicatorColor,
includes: <Offset>[ includes: <Offset>[
const Offset(0.0, -49.0), const Offset(0.0, -49.0),
const Offset(90.0, -49.0), const Offset(68.0, -49.0),
const Offset(-24.0, -49.0), const Offset(-24.0, -49.0),
], ],
excludes: <Offset>[ excludes: <Offset>[
const Offset(98.0, -32.0), // inside full size, outside small const Offset(98.0, -32.0), // inside full size, outside small
const Offset(-16.0, -32.0), // inside full size, outside small const Offset(-40.0, -32.0), // inside full size, outside small
const Offset(90.1, -49.0), const Offset(90.1, -49.0),
const Offset(-24.1, -49.0), const Offset(-40.1, -49.0),
], ],
), ),
); );
...@@ -436,10 +411,9 @@ void main() { ...@@ -436,10 +411,9 @@ void main() {
color: sliderTheme.valueIndicatorColor, color: sliderTheme.valueIndicatorColor,
includes: <Offset>[ includes: <Offset>[
const Offset(0.0, -38.8), const Offset(0.0, -38.8),
const Offset(98.0, -38.8), const Offset(92.0, -38.8),
const Offset(-16.0, -38.8), const Offset(8.0, -23.0), // Inside large, outside scale=1.0
const Offset(10.0, -23.0), // Inside large, outside scale=1.0 const Offset(-2.0, -23.0), // Inside large, outside scale=1.0
const Offset(-4.0, -23.0), // Inside large, outside scale=1.0
], ],
excludes: <Offset>[ excludes: <Offset>[
const Offset(98.5, -38.8), const Offset(98.5, -38.8),
...@@ -461,8 +435,8 @@ void main() { ...@@ -461,8 +435,8 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..rect(rect: Rect.fromLTRB(16.0, 292.0, 208.0, 308.0), color: sliderTheme.activeTrackColor) ..rect(rect: Rect.fromLTRB(32.0, 292.0, 202.0, 308.0), color: sliderTheme.activeTrackColor)
..rect(rect: Rect.fromLTRB(208.0, 292.0, 784.0, 308.0), color: sliderTheme.inactiveTrackColor), ..rect(rect: Rect.fromLTRB(222.0, 292.0, 776.0, 308.0), color: sliderTheme.inactiveTrackColor),
); );
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
...@@ -473,8 +447,8 @@ void main() { ...@@ -473,8 +447,8 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..rect(rect: Rect.fromLTRB(16.0, 292.0, 202.0, 308.0), color: sliderTheme.disabledActiveTrackColor) ..rect(rect: Rect.fromLTRB(32.0, 292.0, 202.0, 308.0), color: sliderTheme.disabledActiveTrackColor)
..rect(rect: Rect.fromLTRB(214.0, 292.0, 784.0, 308.0), color: sliderTheme.disabledInactiveTrackColor), ..rect(rect: Rect.fromLTRB(222.0, 292.0, 776.0, 308.0), color: sliderTheme.disabledInactiveTrackColor),
); );
}); });
...@@ -491,7 +465,7 @@ void main() { ...@@ -491,7 +465,7 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints..circle(x: 208, y: 300, radius: 7, color: sliderTheme.thumbColor), paints..circle(x: 212, y: 300, radius: 7, color: sliderTheme.thumbColor),
); );
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
...@@ -499,7 +473,7 @@ void main() { ...@@ -499,7 +473,7 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints..circle(x: 208, y: 300, radius: 11, color: sliderTheme.disabledThumbColor), paints..circle(x: 212, y: 300, radius: 11, color: sliderTheme.disabledThumbColor),
); );
}); });
...@@ -515,26 +489,21 @@ void main() { ...@@ -515,26 +489,21 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints..circle(x: 208, y: 300, radius: 9, color: sliderTheme.thumbColor), paints..circle(x: 212, y: 300, radius: 9, color: sliderTheme.thumbColor),
); );
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
await tester.pumpAndSettle(); // wait for disable animation await tester.pumpAndSettle(); // wait for disable animation
// Radius should be 6, or 2/3 of 9. 2/3 because the default disabled thumb
// radius is 4 and the default enabled thumb radius is 6.
// TODO(clocksmith): This ratio will change once thumb sizes are updated to spec.
expect( expect(
sliderBox, sliderBox,
paints..circle(x: 208, y: 300, radius: 6, color: sliderTheme.disabledThumbColor), paints..circle(x: 212, y: 300, radius: 9, color: sliderTheme.disabledThumbColor),
); );
}); });
testWidgets('The default slider tick mark shape size can be overridden', (WidgetTester tester) async { testWidgets('The default slider tick mark shape size can be overridden', (WidgetTester tester) async {
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith( final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
tickMarkShape: const RoundSliderTickMarkShape( tickMarkShape: const RoundSliderTickMarkShape(tickMarkRadius: 5),
tickMarkRadius: 5
),
activeTickMarkColor: const Color(0xfadedead), activeTickMarkColor: const Color(0xfadedead),
inactiveTickMarkColor: const Color(0xfadebeef), inactiveTickMarkColor: const Color(0xfadebeef),
disabledActiveTickMarkColor: const Color(0xfadecafe), disabledActiveTickMarkColor: const Color(0xfadecafe),
...@@ -548,9 +517,9 @@ void main() { ...@@ -548,9 +517,9 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..circle(x: 21, y: 300, radius: 5, color: sliderTheme.activeTickMarkColor) ..circle(x: 29, y: 300, radius: 5, color: sliderTheme.activeTickMarkColor)
..circle(x: 400, y: 300, radius: 5, color: sliderTheme.activeTickMarkColor) ..circle(x: 400, y: 300, radius: 5, color: sliderTheme.activeTickMarkColor)
..circle(x: 779, y: 300, radius: 5, color: sliderTheme.inactiveTickMarkColor), ..circle(x: 771, y: 300, radius: 5, color: sliderTheme.inactiveTickMarkColor),
); );
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2, enabled: false)); await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2, enabled: false));
...@@ -559,9 +528,9 @@ void main() { ...@@ -559,9 +528,9 @@ void main() {
expect( expect(
sliderBox, sliderBox,
paints paints
..circle(x: 21, y: 300, radius: 5, color: sliderTheme.disabledActiveTickMarkColor) ..circle(x: 29, y: 300, radius: 5, color: sliderTheme.disabledActiveTickMarkColor)
..circle(x: 400, y: 300, radius: 5, color: sliderTheme.disabledActiveTickMarkColor) ..circle(x: 400, y: 300, radius: 5, color: sliderTheme.disabledActiveTickMarkColor)
..circle(x: 779, y: 300, radius: 5, color: sliderTheme.disabledInactiveTickMarkColor), ..circle(x: 771, y: 300, radius: 5, color: sliderTheme.disabledInactiveTickMarkColor),
); );
}); });
......
...@@ -56,16 +56,6 @@ void main() { ...@@ -56,16 +56,6 @@ void main() {
expect(darkTheme.accentTextTheme.title.color, typography.white.title.color); expect(darkTheme.accentTextTheme.title.color, typography.white.title.color);
}); });
test('Default slider indicator style gets a default body2 if accentTextTheme.body2 is null', () {
const TextTheme noBody2TextTheme = TextTheme(body2: null);
final ThemeData lightTheme = ThemeData(brightness: Brightness.light, accentTextTheme: noBody2TextTheme);
final ThemeData darkTheme = ThemeData(brightness: Brightness.dark, accentTextTheme: noBody2TextTheme);
final Typography typography = Typography(platform: lightTheme.platform);
expect(lightTheme.sliderTheme.valueIndicatorTextStyle, equals(typography.white.body2));
expect(darkTheme.sliderTheme.valueIndicatorTextStyle, equals(typography.black.body2));
});
test('Default chip label style gets a default body2 if textTheme.body2 is null', () { test('Default chip label style gets a default body2 if textTheme.body2 is null', () {
const TextTheme noBody2TextTheme = TextTheme(body2: null); const TextTheme noBody2TextTheme = TextTheme(body2: null);
final ThemeData lightTheme = ThemeData(brightness: Brightness.light, textTheme: noBody2TextTheme); final ThemeData lightTheme = ThemeData(brightness: Brightness.light, textTheme: noBody2TextTheme);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment