[Material] Allow slider shapes to be easily resized (#27510)
* Add size configs for round default shapes, and add tests, for all shapes that can be sized without creating new custom shape painters
This commit is contained in:
parent
3dbec84052
commit
b356b94aef
@ -846,9 +846,16 @@ abstract class SliderComponentShape {
|
|||||||
/// * [SliderTrackShape] Base component for creating other custom track
|
/// * [SliderTrackShape] Base component for creating other custom track
|
||||||
/// shapes.
|
/// shapes.
|
||||||
class RectangularSliderTrackShape extends SliderTrackShape {
|
class RectangularSliderTrackShape extends SliderTrackShape {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
/// Create a slider track that draws 2 rectangles.
|
||||||
/// const constructors so that they can be used in const expressions.
|
const RectangularSliderTrackShape({ this.disabledThumbGapWidth = 2.0 });
|
||||||
const RectangularSliderTrackShape();
|
|
||||||
|
/// Horizontal spacing, or gap, between the disabled thumb and the track.
|
||||||
|
///
|
||||||
|
/// This is only used when the slider is disabled. There is no gap around
|
||||||
|
/// the thumb and any part of the track when the slider is enabled. The
|
||||||
|
/// Material spec defaults this gap width 2, which is half of the disabled
|
||||||
|
/// thumb radius.
|
||||||
|
final double disabledThumbGapWidth;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Rect getPreferredRect({
|
Rect getPreferredRect({
|
||||||
@ -874,8 +881,6 @@ class RectangularSliderTrackShape extends SliderTrackShape {
|
|||||||
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
|
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spacing for disabled slider state.
|
|
||||||
static const double _thumbGap = 2.0;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(
|
void paint(
|
||||||
@ -909,11 +914,15 @@ class RectangularSliderTrackShape extends SliderTrackShape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Used to create a gap around the thumb iff the slider is disabled.
|
// 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;
|
double horizontalAdjustment = 0.0;
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
final double thumbRadius = sliderTheme.thumbShape.getPreferredSize(isEnabled, isDiscrete).width / 2.0;
|
final double disabledThumbRadius = sliderTheme.thumbShape.getPreferredSize(false, isDiscrete).width / 2.0;
|
||||||
final double gap = _thumbGap * (1.0 - enableAnimation.value);
|
final double gap = disabledThumbGapWidth * (1.0 - enableAnimation.value);
|
||||||
horizontalAdjustment = thumbRadius + gap;
|
horizontalAdjustment = disabledThumbRadius + gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Rect trackRect = getPreferredRect(
|
final Rect trackRect = getPreferredRect(
|
||||||
@ -949,16 +958,22 @@ class RectangularSliderTrackShape extends SliderTrackShape {
|
|||||||
/// * [SliderTheme], which can be used to configure the tick mark shape of all
|
/// * [SliderTheme], which can be used to configure the tick mark shape of all
|
||||||
/// sliders in a widget subtree.
|
/// sliders in a widget subtree.
|
||||||
class RoundSliderTickMarkShape extends SliderTickMarkShape {
|
class RoundSliderTickMarkShape extends SliderTickMarkShape {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
/// Create a slider tick mark that draws a circle.
|
||||||
/// const constructors so that they can be used in const expressions.
|
const RoundSliderTickMarkShape({ this.tickMarkRadius });
|
||||||
const RoundSliderTickMarkShape();
|
|
||||||
|
/// The preferred radius of the round tick mark.
|
||||||
|
///
|
||||||
|
/// If it is not provided, then half of the track height is used.
|
||||||
|
final double tickMarkRadius;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size getPreferredSize({
|
Size getPreferredSize({
|
||||||
bool isEnabled,
|
bool isEnabled,
|
||||||
SliderThemeData sliderTheme,
|
SliderThemeData sliderTheme,
|
||||||
}) {
|
}) {
|
||||||
return Size.fromRadius(sliderTheme.trackHeight / 2);
|
// The tick marks are tiny circles. If no radius is provided, then they are
|
||||||
|
// defaulted to be the same height as the track.
|
||||||
|
return Size.fromRadius(tickMarkRadius ?? sliderTheme.trackHeight / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -991,7 +1006,10 @@ class RoundSliderTickMarkShape extends SliderTickMarkShape {
|
|||||||
final Paint paint = Paint()..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation);
|
final Paint paint = Paint()..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation);
|
||||||
|
|
||||||
// The tick marks are tiny circles that are the same height as the track.
|
// The tick marks are tiny circles that are the same height as the track.
|
||||||
final double tickMarkRadius = sliderTheme.trackHeight / 2;
|
final double tickMarkRadius = getPreferredSize(
|
||||||
|
isEnabled: isEnabled,
|
||||||
|
sliderTheme: sliderTheme,
|
||||||
|
).width / 2;
|
||||||
context.canvas.drawCircle(center, tickMarkRadius, paint);
|
context.canvas.drawCircle(center, tickMarkRadius, paint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1005,14 +1023,30 @@ 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.
|
||||||
const RoundSliderThumbShape();
|
// TODO(clocksmith): This needs to be changed to 10 according to spec.
|
||||||
|
const RoundSliderThumbShape({
|
||||||
|
this.enabledThumbRadius = 6.0,
|
||||||
|
this.disabledThumbRadius
|
||||||
|
});
|
||||||
|
|
||||||
static const double _thumbRadius = 6.0;
|
/// The preferred radius of the round thumb shape when the slider is enabled.
|
||||||
static const double _disabledThumbRadius = 4.0;
|
///
|
||||||
|
/// If it is not provided, then the material default is used.
|
||||||
|
final double enabledThumbRadius;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// thumb radius and has the same ratio of enabled size to disabled size as
|
||||||
|
/// the Material spec. The default resolves to 4, which is 2 / 3 of the
|
||||||
|
/// default enabled thumb.
|
||||||
|
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 * 2 / 3;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
|
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
|
||||||
return Size.fromRadius(isEnabled ? _thumbRadius : _disabledThumbRadius);
|
return Size.fromRadius(isEnabled ? enabledThumbRadius : _disabledThumbRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -1031,7 +1065,7 @@ class RoundSliderThumbShape extends SliderComponentShape {
|
|||||||
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,
|
||||||
end: _thumbRadius,
|
end: enabledThumbRadius,
|
||||||
);
|
);
|
||||||
final ColorTween colorTween = ColorTween(
|
final ColorTween colorTween = ColorTween(
|
||||||
begin: sliderTheme.disabledThumbColor,
|
begin: sliderTheme.disabledThumbColor,
|
||||||
@ -1062,13 +1096,17 @@ 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.
|
||||||
const RoundSliderOverlayShape();
|
// TODO(clocksmith): This needs to be changed to 24 according to spec.
|
||||||
|
const RoundSliderOverlayShape({ this.overlayRadius = 16.0 });
|
||||||
|
|
||||||
static const double _overlayRadius = 16.0;
|
/// The preferred radius of the round thumb shape when enabled.
|
||||||
|
///
|
||||||
|
/// If it is not provided, then half of the track height is used.
|
||||||
|
final double overlayRadius;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
|
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
|
||||||
return const Size.fromRadius(_overlayRadius);
|
return Size.fromRadius(overlayRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -1087,7 +1125,7 @@ class RoundSliderOverlayShape extends SliderComponentShape {
|
|||||||
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.
|
// TODO(gspencer): We don't really follow the spec here for overlays.
|
||||||
|
@ -30,32 +30,15 @@ void main() {
|
|||||||
);
|
);
|
||||||
final SliderThemeData sliderTheme = theme.sliderTheme;
|
final SliderThemeData sliderTheme = theme.sliderTheme;
|
||||||
|
|
||||||
Widget buildSlider(SliderThemeData data) {
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, enabled: false));
|
||||||
return Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: MediaQuery(
|
|
||||||
data: MediaQueryData.fromWindow(window),
|
|
||||||
child: Material(
|
|
||||||
child: Center(
|
|
||||||
child: Theme(
|
|
||||||
data: theme,
|
|
||||||
child: const Slider(
|
|
||||||
value: 0.5,
|
|
||||||
label: '0.5',
|
|
||||||
onChanged: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await tester.pumpWidget(buildSlider(sliderTheme));
|
|
||||||
|
|
||||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
expect(sliderBox, paints..rect(color: sliderTheme.disabledActiveTrackColor)..rect(color: sliderTheme.disabledInactiveTrackColor));
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints
|
||||||
|
..rect(color: sliderTheme.disabledActiveTrackColor)
|
||||||
|
..rect(color: sliderTheme.disabledInactiveTrackColor),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Slider overrides ThemeData theme if SliderTheme present', (WidgetTester tester) async {
|
testWidgets('Slider overrides ThemeData theme if SliderTheme present', (WidgetTester tester) async {
|
||||||
@ -69,35 +52,15 @@ void main() {
|
|||||||
inactiveTrackColor: Colors.purple.withAlpha(0x3d),
|
inactiveTrackColor: Colors.purple.withAlpha(0x3d),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget buildSlider(SliderThemeData data) {
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, enabled: false));
|
||||||
return Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: MediaQuery(
|
|
||||||
data: MediaQueryData.fromWindow(window),
|
|
||||||
child: Material(
|
|
||||||
child: Center(
|
|
||||||
child: Theme(
|
|
||||||
data: theme,
|
|
||||||
child: SliderTheme(
|
|
||||||
data: customTheme,
|
|
||||||
child: const Slider(
|
|
||||||
value: 0.5,
|
|
||||||
label: '0.5',
|
|
||||||
onChanged: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await tester.pumpWidget(buildSlider(sliderTheme));
|
|
||||||
|
|
||||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
expect(sliderBox, paints..rect(color: customTheme.disabledActiveTrackColor)..rect(color: customTheme.disabledInactiveTrackColor));
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints
|
||||||
|
..rect(color: customTheme.disabledActiveTrackColor)
|
||||||
|
..rect(color: customTheme.disabledInactiveTrackColor),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('SliderThemeData assigns the correct default shapes', (WidgetTester tester) async {
|
testWidgets('SliderThemeData assigns the correct default shapes', (WidgetTester tester) async {
|
||||||
@ -180,33 +143,12 @@ void main() {
|
|||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
);
|
);
|
||||||
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500);
|
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500);
|
||||||
double value = 0.25;
|
|
||||||
Widget buildApp({ bool enabled = true }) {
|
|
||||||
final ValueChanged<double> onChanged = enabled ? (double d) => value = d : null;
|
|
||||||
return Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: MediaQuery(
|
|
||||||
data: MediaQueryData.fromWindow(window),
|
|
||||||
child: Material(
|
|
||||||
child: Center(
|
|
||||||
child: SliderTheme(
|
|
||||||
data: sliderTheme,
|
|
||||||
child: Slider(
|
|
||||||
value: value,
|
|
||||||
label: '$value',
|
|
||||||
onChanged: onChanged,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp());
|
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25));
|
||||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
// The enabled slider thumb has track segments that extend to and from
|
||||||
|
// the center of the thumb.
|
||||||
expect(
|
expect(
|
||||||
sliderBox,
|
sliderBox,
|
||||||
paints
|
paints
|
||||||
@ -214,10 +156,15 @@ void main() {
|
|||||||
..rect(rect: Rect.fromLTRB(208.0, 299.0, 784.0, 301.0), color: sliderTheme.inactiveTrackColor)
|
..rect(rect: Rect.fromLTRB(208.0, 299.0, 784.0, 301.0), color: sliderTheme.inactiveTrackColor)
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp(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
|
||||||
// The disabled thumb is smaller so the track has to paint longer to get
|
|
||||||
// to the edge.
|
// The disabled slider thumb has a horizontal gap between itself and the
|
||||||
|
// track segments. Therefore, the track segments are shorter since they do
|
||||||
|
// not extend to the center of the thumb, but rather the outer edge of th
|
||||||
|
// gap. As a result, the `right` value of the first segment is less than it
|
||||||
|
// is above, and the `left` value of the second segment is more than it is
|
||||||
|
// above.
|
||||||
expect(
|
expect(
|
||||||
sliderBox,
|
sliderBox,
|
||||||
paints
|
paints
|
||||||
@ -232,31 +179,8 @@ void main() {
|
|||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
);
|
);
|
||||||
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500);
|
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500);
|
||||||
double value = 0.25;
|
|
||||||
Widget buildApp({ bool enabled = true }) {
|
|
||||||
final ValueChanged<double> onChanged = enabled ? (double d) => value = d : null;
|
|
||||||
return Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: MediaQuery(
|
|
||||||
data: MediaQueryData.fromWindow(window),
|
|
||||||
child: Material(
|
|
||||||
child: Center(
|
|
||||||
child: SliderTheme(
|
|
||||||
data: sliderTheme,
|
|
||||||
child: Slider(
|
|
||||||
value: value,
|
|
||||||
label: '$value',
|
|
||||||
onChanged: onChanged,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp());
|
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25));
|
||||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
// With no touch, paints only the thumb.
|
// With no touch, paints only the thumb.
|
||||||
@ -296,6 +220,7 @@ void main() {
|
|||||||
|
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
// After the gesture is up and complete, it again paints only the thumb.
|
// After the gesture is up and complete, it again paints only the thumb.
|
||||||
expect(
|
expect(
|
||||||
sliderBox,
|
sliderBox,
|
||||||
@ -315,45 +240,20 @@ void main() {
|
|||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
);
|
);
|
||||||
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500);
|
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500);
|
||||||
double value = 0.45;
|
|
||||||
Widget buildApp({
|
|
||||||
int divisions,
|
|
||||||
bool enabled = true,
|
|
||||||
}) {
|
|
||||||
final ValueChanged<double> onChanged = enabled ? (double d) => value = d : null;
|
|
||||||
return Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: MediaQuery(
|
|
||||||
data: MediaQueryData.fromWindow(window),
|
|
||||||
child: Material(
|
|
||||||
child: Center(
|
|
||||||
child: SliderTheme(
|
|
||||||
data: sliderTheme,
|
|
||||||
child: Slider(
|
|
||||||
value: value,
|
|
||||||
label: '$value',
|
|
||||||
divisions: divisions,
|
|
||||||
onChanged: onChanged,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp());
|
|
||||||
|
|
||||||
|
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: 6.0));
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp(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: 4.0));
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp(divisions: 3));
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, divisions: 3));
|
||||||
await tester.pumpAndSettle(); // wait for disable animation
|
await tester.pumpAndSettle(); // wait for enable animation
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
sliderBox,
|
sliderBox,
|
||||||
paints
|
paints
|
||||||
@ -364,8 +264,9 @@ void main() {
|
|||||||
..circle(color: sliderTheme.thumbColor, radius: 6.0)
|
..circle(color: sliderTheme.thumbColor, radius: 6.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp(divisions: 3, enabled: false));
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.45, divisions: 3, enabled: false));
|
||||||
await tester.pumpAndSettle(); // wait for disable animation
|
await tester.pumpAndSettle(); // wait for disable animation
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
sliderBox,
|
sliderBox,
|
||||||
paints
|
paints
|
||||||
@ -548,4 +449,169 @@ void main() {
|
|||||||
);
|
);
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('The slider track height can be overridden', (WidgetTester tester) async {
|
||||||
|
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(trackHeight: 16);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
// Top and bottom are centerY (300) + and - trackRadius (8).
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints
|
||||||
|
..rect(rect: Rect.fromLTRB(16.0, 292.0, 208.0, 308.0), color: sliderTheme.activeTrackColor)
|
||||||
|
..rect(rect: Rect.fromLTRB(208.0, 292.0, 784.0, 308.0), color: sliderTheme.inactiveTrackColor)
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
|
||||||
|
await tester.pumpAndSettle(); // wait for disable animation
|
||||||
|
|
||||||
|
// The disabled thumb is smaller so the active track has to paint longer to
|
||||||
|
// get to the edge.
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints
|
||||||
|
..rect(rect: Rect.fromLTRB(16.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)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The default slider thumb shape sizes can be overridden', (WidgetTester tester) async {
|
||||||
|
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||||
|
thumbShape: const RoundSliderThumbShape(
|
||||||
|
enabledThumbRadius: 7,
|
||||||
|
disabledThumbRadius: 11,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25));
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints..circle(x: 208, y: 300, radius: 7, color: sliderTheme.thumbColor)
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
|
||||||
|
await tester.pumpAndSettle(); // wait for disable animation
|
||||||
|
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints..circle(x: 208, y: 300, radius: 11, color: sliderTheme.disabledThumbColor)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The default slider thumb shape disabled size can be inferred from the enabled size', (WidgetTester tester) async {
|
||||||
|
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||||
|
thumbShape: const RoundSliderThumbShape(
|
||||||
|
enabledThumbRadius: 9,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25));
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints..circle(x: 208, y: 300, radius: 9, color: sliderTheme.thumbColor)
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
|
||||||
|
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(
|
||||||
|
sliderBox,
|
||||||
|
paints..circle(x: 208, y: 300, radius: 6, color: sliderTheme.disabledThumbColor)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
testWidgets('The default slider tick mark shape size can be overridden', (WidgetTester tester) async {
|
||||||
|
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||||
|
tickMarkShape: const RoundSliderTickMarkShape(
|
||||||
|
tickMarkRadius: 5
|
||||||
|
),
|
||||||
|
activeTickMarkColor: const Color(0xfadedead),
|
||||||
|
inactiveTickMarkColor: const Color(0xfadebeef),
|
||||||
|
disabledActiveTickMarkColor: const Color(0xfadecafe),
|
||||||
|
disabledInactiveTickMarkColor: const Color(0xfadeface),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2));
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints
|
||||||
|
..circle(x: 21, 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)
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2, enabled: false));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints
|
||||||
|
..circle(x: 21, 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)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('The default slider overlay shape size can be overridden', (WidgetTester tester) async {
|
||||||
|
const double uniqueOverlayRadius = 23;
|
||||||
|
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||||
|
overlayShape: const RoundSliderOverlayShape(
|
||||||
|
overlayRadius: uniqueOverlayRadius,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5));
|
||||||
|
// Tap center and wait for animation.
|
||||||
|
final Offset center = tester.getCenter(find.byType(Slider));
|
||||||
|
await tester.startGesture(center);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||||
|
expect(
|
||||||
|
sliderBox,
|
||||||
|
paints..circle(
|
||||||
|
x: center.dx,
|
||||||
|
y: center.dy,
|
||||||
|
radius: uniqueOverlayRadius,
|
||||||
|
color: sliderTheme.overlayColor,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildApp(
|
||||||
|
SliderThemeData sliderTheme, {
|
||||||
|
double value = 0.0,
|
||||||
|
bool enabled = true,
|
||||||
|
int divisions,
|
||||||
|
}) {
|
||||||
|
final ValueChanged<double> onChanged = enabled ? (double d) => value = d : null;
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: SliderTheme(
|
||||||
|
data: sliderTheme,
|
||||||
|
child: Slider(
|
||||||
|
value: value,
|
||||||
|
label: '$value',
|
||||||
|
onChanged: onChanged,
|
||||||
|
divisions: divisions,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user