mirror of
https://github.com/flutter/flutter
synced 2024-08-28 04:21:14 +00:00
Add Material 3 support for Slider
- Part 2 (#114624)
* Add Material 3 support for Slider - Part 2 * Kick tests * Update drawing order to fix html renderer bug * Update test
This commit is contained in:
parent
0344407614
commit
ac06523b74
|
@ -71,6 +71,14 @@ class _${blockName}DefaultsM3 extends SliderThemeData {
|
|||
|
||||
return Colors.transparent;
|
||||
});
|
||||
|
||||
@override
|
||||
TextStyle? get valueIndicatorTextStyle => ${textStyle('$tokenGroup.label.label-text')}!.copyWith(
|
||||
color: ${componentColor('$tokenGroup.label.label-text')},
|
||||
);
|
||||
|
||||
@override
|
||||
SliderComponentShape? get valueIndicatorShape => const DropSliderValueIndicatorShape();
|
||||
}
|
||||
''';
|
||||
|
||||
|
|
|
@ -759,7 +759,7 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
|||
const SliderTickMarkShape defaultTickMarkShape = RoundSliderTickMarkShape();
|
||||
const SliderComponentShape defaultOverlayShape = RoundSliderOverlayShape();
|
||||
const SliderComponentShape defaultThumbShape = RoundSliderThumbShape();
|
||||
const SliderComponentShape defaultValueIndicatorShape = RectangularSliderValueIndicatorShape();
|
||||
final SliderComponentShape defaultValueIndicatorShape = defaults.valueIndicatorShape!;
|
||||
const ShowValueIndicator defaultShowValueIndicator = ShowValueIndicator.onlyForDiscrete;
|
||||
|
||||
final Set<MaterialState> states = <MaterialState>{
|
||||
|
@ -810,9 +810,7 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
|||
overlayShape: sliderTheme.overlayShape ?? defaultOverlayShape,
|
||||
valueIndicatorShape: valueIndicatorShape,
|
||||
showValueIndicator: sliderTheme.showValueIndicator ?? defaultShowValueIndicator,
|
||||
valueIndicatorTextStyle: sliderTheme.valueIndicatorTextStyle ?? theme.textTheme.bodyLarge!.copyWith(
|
||||
color: theme.colorScheme.onPrimary,
|
||||
),
|
||||
valueIndicatorTextStyle: sliderTheme.valueIndicatorTextStyle ?? defaults.valueIndicatorTextStyle,
|
||||
);
|
||||
final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor?>(widget.mouseCursor, states)
|
||||
?? sliderTheme.mouseCursor?.resolve(states)
|
||||
|
@ -851,6 +849,14 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
|||
break;
|
||||
}
|
||||
|
||||
final double textScaleFactor = theme.useMaterial3
|
||||
// TODO(tahatesser): This is an eye-balled value.
|
||||
// This needs to be updated when accessibility
|
||||
// guidelines are available on the material specs page
|
||||
// https://m3.material.io/components/sliders/accessibility.
|
||||
? math.min(MediaQuery.of(context).textScaleFactor, 1.3)
|
||||
: MediaQuery.of(context).textScaleFactor;
|
||||
|
||||
return Semantics(
|
||||
container: true,
|
||||
slider: true,
|
||||
|
@ -873,7 +879,7 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
|||
divisions: widget.divisions,
|
||||
label: widget.label,
|
||||
sliderTheme: sliderTheme,
|
||||
textScaleFactor: MediaQuery.of(context).textScaleFactor,
|
||||
textScaleFactor: textScaleFactor,
|
||||
screenSize: screenSize(),
|
||||
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
|
||||
onChangeStart: _handleDragStart,
|
||||
|
@ -1858,6 +1864,14 @@ class _SliderDefaultsM2 extends SliderThemeData {
|
|||
|
||||
@override
|
||||
Color? get overlayColor => _colors.primary.withOpacity(0.12);
|
||||
|
||||
@override
|
||||
TextStyle? get valueIndicatorTextStyle => Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
color: _colors.onPrimary,
|
||||
);
|
||||
|
||||
@override
|
||||
SliderComponentShape? get valueIndicatorShape => const RectangularSliderValueIndicatorShape();
|
||||
}
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES - Slider
|
||||
|
@ -1927,6 +1941,14 @@ class _SliderDefaultsM3 extends SliderThemeData {
|
|||
|
||||
return Colors.transparent;
|
||||
});
|
||||
|
||||
@override
|
||||
TextStyle? get valueIndicatorTextStyle => Theme.of(context).textTheme.labelMedium!.copyWith(
|
||||
color: _colors.onPrimary,
|
||||
);
|
||||
|
||||
@override
|
||||
SliderComponentShape? get valueIndicatorShape => const DropSliderValueIndicatorShape();
|
||||
}
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES - Slider
|
||||
|
|
|
@ -3483,3 +3483,185 @@ void _debugDrawShadow(Canvas canvas, Path path, double elevation) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The default shape of a Material 3 [Slider]'s value indicator.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Slider], which includes a value indicator defined by this shape.
|
||||
/// * [SliderTheme], which can be used to configure the slider value indicator
|
||||
/// of all sliders in a widget subtree.
|
||||
class DropSliderValueIndicatorShape extends SliderComponentShape {
|
||||
/// Create a slider value indicator that resembles a drop shape.
|
||||
const DropSliderValueIndicatorShape();
|
||||
|
||||
static const _DropSliderValueIndicatorPathPainter _pathPainter = _DropSliderValueIndicatorPathPainter();
|
||||
|
||||
@override
|
||||
Size getPreferredSize(
|
||||
bool isEnabled,
|
||||
bool isDiscrete, {
|
||||
TextPainter? labelPainter,
|
||||
double? textScaleFactor,
|
||||
}) {
|
||||
assert(labelPainter != null);
|
||||
assert(textScaleFactor != null && textScaleFactor >= 0);
|
||||
return _pathPainter.getPreferredSize(labelPainter!, textScaleFactor!);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(
|
||||
PaintingContext context,
|
||||
Offset center, {
|
||||
required Animation<double> activationAnimation,
|
||||
required Animation<double> enableAnimation,
|
||||
required bool isDiscrete,
|
||||
required TextPainter labelPainter,
|
||||
required RenderBox parentBox,
|
||||
required SliderThemeData sliderTheme,
|
||||
required TextDirection textDirection,
|
||||
required double value,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final double scale = activationAnimation.value;
|
||||
_pathPainter.paint(
|
||||
parentBox: parentBox,
|
||||
canvas: canvas,
|
||||
center: center,
|
||||
scale: scale,
|
||||
labelPainter: labelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: sizeWithOverflow,
|
||||
backgroundPaintColor: sliderTheme.valueIndicatorColor!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DropSliderValueIndicatorPathPainter {
|
||||
const _DropSliderValueIndicatorPathPainter();
|
||||
|
||||
static const double _triangleHeight = 10.0;
|
||||
static const double _labelPadding = 8.0;
|
||||
static const double _preferredHeight = 32.0;
|
||||
static const double _minLabelWidth = 20.0;
|
||||
static const double _minRectHeight = 28.0;
|
||||
static const double _rectYOffset = 6.0;
|
||||
static const double _bottomTipYOffset = 16.0;
|
||||
static const double _preferredHalfHeight = _preferredHeight / 2;
|
||||
static const double _upperRectRadius = 4;
|
||||
|
||||
Size getPreferredSize(
|
||||
TextPainter labelPainter,
|
||||
double textScaleFactor,
|
||||
) {
|
||||
assert(labelPainter != null);
|
||||
final double width = math.max(_minLabelWidth, labelPainter.width) + _labelPadding * 2 * textScaleFactor;
|
||||
return Size(width, _preferredHeight * textScaleFactor);
|
||||
}
|
||||
|
||||
double getHorizontalShift({
|
||||
required RenderBox parentBox,
|
||||
required Offset center,
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
required double scale,
|
||||
}) {
|
||||
assert(!sizeWithOverflow.isEmpty);
|
||||
|
||||
const double edgePadding = 8.0;
|
||||
final double rectangleWidth = _upperRectangleWidth(labelPainter, scale);
|
||||
/// Value indicator draws on the Overlay and by using the global Offset
|
||||
/// we are making sure we use the bounds of the Overlay instead of the Slider.
|
||||
final Offset globalCenter = parentBox.localToGlobal(center);
|
||||
|
||||
// The rectangle must be shifted towards the center so that it minimizes the
|
||||
// chance of it rendering outside the bounds of the render box. If the shift
|
||||
// is negative, then the lobe is shifted from right to left, and if it is
|
||||
// positive, then the lobe is shifted from left to right.
|
||||
final double overflowLeft = math.max(0, rectangleWidth / 2 - globalCenter.dx + edgePadding);
|
||||
final double overflowRight = math.max(0, rectangleWidth / 2 - (sizeWithOverflow.width - globalCenter.dx - edgePadding));
|
||||
|
||||
if (rectangleWidth < sizeWithOverflow.width) {
|
||||
return overflowLeft - overflowRight;
|
||||
} else if (overflowLeft - overflowRight > 0) {
|
||||
return overflowLeft - (edgePadding * textScaleFactor);
|
||||
} else {
|
||||
return -overflowRight + (edgePadding * textScaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
double _upperRectangleWidth(TextPainter labelPainter, double scale) {
|
||||
final double unscaledWidth = math.max(_minLabelWidth, labelPainter.width) + _labelPadding;
|
||||
return unscaledWidth * scale;
|
||||
}
|
||||
|
||||
BorderRadius _adjustBorderRadius(Rect rect) {
|
||||
const double rectness = 0.0;
|
||||
return BorderRadius.lerp(
|
||||
BorderRadius.circular(_upperRectRadius),
|
||||
BorderRadius.all(Radius.circular(rect.shortestSide / 2.0)),
|
||||
1.0 - rectness,
|
||||
)!;
|
||||
}
|
||||
|
||||
void paint({
|
||||
required RenderBox parentBox,
|
||||
required Canvas canvas,
|
||||
required Offset center,
|
||||
required double scale,
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
required Color backgroundPaintColor,
|
||||
Color? strokePaintColor,
|
||||
}) {
|
||||
if (scale == 0.0) {
|
||||
// Zero scale essentially means "do not draw anything", so it's safe to just return.
|
||||
return;
|
||||
}
|
||||
assert(!sizeWithOverflow.isEmpty);
|
||||
|
||||
final double rectangleWidth = _upperRectangleWidth(labelPainter, scale);
|
||||
final double horizontalShift = getHorizontalShift(
|
||||
parentBox: parentBox,
|
||||
center: center,
|
||||
labelPainter: labelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: sizeWithOverflow,
|
||||
scale: scale,
|
||||
);
|
||||
final Rect upperRect = Rect.fromLTWH(
|
||||
-rectangleWidth / 2 + horizontalShift,
|
||||
-_rectYOffset - _minRectHeight,
|
||||
rectangleWidth,
|
||||
_minRectHeight,
|
||||
);
|
||||
|
||||
final Paint fillPaint = Paint()..color = backgroundPaintColor;
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(center.dx, center.dy - _bottomTipYOffset);
|
||||
canvas.scale(scale, scale);
|
||||
|
||||
final BorderRadius adjustedBorderRadius = _adjustBorderRadius(upperRect);
|
||||
final RRect borderRect = adjustedBorderRadius.resolve(labelPainter.textDirection).toRRect(upperRect);
|
||||
final Path trianglePath = Path()
|
||||
..lineTo(-_triangleHeight, -_triangleHeight)
|
||||
..lineTo(_triangleHeight, -_triangleHeight)
|
||||
..close();
|
||||
canvas.drawPath(trianglePath, fillPaint);
|
||||
canvas.drawRRect(borderRect, fillPaint);
|
||||
|
||||
// The label text is centered within the value indicator.
|
||||
final double bottomTipToUpperRectTranslateY = -_preferredHalfHeight / 2 - upperRect.height;
|
||||
canvas.translate(0, bottomTipToUpperRectTranslateY);
|
||||
final Offset boxCenter = Offset(horizontalShift, upperRect.height / 1.75);
|
||||
final Offset halfLabelPainterOffset = Offset(labelPainter.width / 2, labelPainter.height / 2);
|
||||
final Offset labelOffset = boxCenter - halfLabelPainterOffset;
|
||||
labelPainter.paint(canvas, labelOffset);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1857,6 +1857,63 @@ void main() {
|
|||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
||||
});
|
||||
|
||||
testWidgets('Default value indicator color', (WidgetTester tester) async {
|
||||
debugDisableShadows = false;
|
||||
try {
|
||||
final ThemeData theme = ThemeData(
|
||||
useMaterial3: true,
|
||||
platform: TargetPlatform.android,
|
||||
);
|
||||
Widget buildApp(String value, { double sliderValue = 0.5, double textScale = 1.0 }) {
|
||||
return MaterialApp(
|
||||
theme: theme,
|
||||
home: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window).copyWith(textScaleFactor: textScale),
|
||||
child: Material(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: sliderValue,
|
||||
label: value,
|
||||
divisions: 3,
|
||||
onChanged: (double d) { },
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildApp('1'));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.renderObject(find.byType(Overlay));
|
||||
|
||||
final Offset center = tester.getCenter(find.byType(Slider));
|
||||
await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect(color: const Color(0xfffffbfe))
|
||||
..rrect(color: const Color(0xff6750a4))
|
||||
..rrect(color: const Color(0xffe7e0ec))
|
||||
..path(color: Color(theme.colorScheme.primary.value))
|
||||
..rrect(
|
||||
color: Color(theme.colorScheme.primary.value),
|
||||
)
|
||||
);
|
||||
|
||||
} finally {
|
||||
debugDisableShadows = true;
|
||||
}
|
||||
});
|
||||
|
||||
group('Material 2', () {
|
||||
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
|
||||
|
@ -2010,6 +2067,61 @@ void main() {
|
|||
debugDisableShadows = true;
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('Default value indicator color', (WidgetTester tester) async {
|
||||
debugDisableShadows = false;
|
||||
try {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
);
|
||||
Widget buildApp(String value, { double sliderValue = 0.5, double textScale = 1.0 }) {
|
||||
return MaterialApp(
|
||||
theme: theme,
|
||||
home: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window).copyWith(textScaleFactor: textScale),
|
||||
child: Material(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: sliderValue,
|
||||
label: value,
|
||||
divisions: 3,
|
||||
onChanged: (double d) { },
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildApp('1'));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.renderObject(find.byType(Overlay));
|
||||
|
||||
final Offset center = tester.getCenter(find.byType(Slider));
|
||||
await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect(color: const Color(0xfffafafa))
|
||||
..rrect(color: const Color(0xff2196f3))
|
||||
..rrect(color: const Color(0x3d2196f3))
|
||||
// Test that the value indicator text is painted with the correct color.
|
||||
..path(color: const Color(0xf55f5f5f))
|
||||
);
|
||||
|
||||
} finally {
|
||||
debugDisableShadows = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,37 +14,40 @@ void main() {
|
|||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_1_width_0.png'),
|
||||
matchesGoldenFile('slider_m3_start_text_scale_1_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0.5,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_1_width_0.png'),
|
||||
matchesGoldenFile('slider_m3_middle_text_scale_1_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 1,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_1_width_0.png'),
|
||||
matchesGoldenFile('slider_m3_end_text_scale_1_width_0.png'),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -53,39 +56,42 @@ void main() {
|
|||
tester,
|
||||
value: 0,
|
||||
decimalCount: 5,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_1_width_5.png'),
|
||||
matchesGoldenFile('slider_m3_start_text_scale_1_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0.5,
|
||||
decimalCount: 5,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_1_width_5.png'),
|
||||
matchesGoldenFile('slider_m3_middle_text_scale_1_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 1,
|
||||
decimalCount: 5,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_1_width_5.png'),
|
||||
matchesGoldenFile('slider_m3_end_text_scale_1_width_5.png'),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -94,39 +100,42 @@ void main() {
|
|||
tester,
|
||||
value: 0,
|
||||
textScale: 3,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_4_width_0.png'),
|
||||
matchesGoldenFile('slider_m3_start_text_scale_4_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0.5,
|
||||
textScale: 3,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_4_width_0.png'),
|
||||
matchesGoldenFile('slider_m3_middle_text_scale_4_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 1,
|
||||
textScale: 3,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_4_width_0.png'),
|
||||
matchesGoldenFile('slider_m3_end_text_scale_4_width_0.png'),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -137,13 +146,14 @@ void main() {
|
|||
value: 0,
|
||||
textScale: 3,
|
||||
decimalCount: 5,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_4_width_5.png'),
|
||||
matchesGoldenFile('slider_m3_start_text_scale_4_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
|
@ -151,13 +161,14 @@ void main() {
|
|||
value: 0.5,
|
||||
textScale: 3,
|
||||
decimalCount: 5,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_4_width_5.png'),
|
||||
matchesGoldenFile('slider_m3_middle_text_scale_4_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
|
@ -165,15 +176,186 @@ void main() {
|
|||
value: 1,
|
||||
textScale: 3,
|
||||
decimalCount: 5,
|
||||
useMaterial3: true,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_4_width_5.png'),
|
||||
matchesGoldenFile('slider_m3_end_text_scale_4_width_5.png'),
|
||||
);
|
||||
});
|
||||
|
||||
group('Material 2', () {
|
||||
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
|
||||
// is turned on by default, these tests can be removed.
|
||||
|
||||
testWidgets('Slider value indicator', (WidgetTester tester) async {
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_1_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0.5,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_1_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 1,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_1_width_0.png'),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider value indicator wide text', (WidgetTester tester) async {
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0,
|
||||
decimalCount: 5,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_1_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0.5,
|
||||
decimalCount: 5,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_1_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 1,
|
||||
decimalCount: 5,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_1_width_5.png'),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider value indicator large text scale', (WidgetTester tester) async {
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0,
|
||||
textScale: 3,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_4_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0.5,
|
||||
textScale: 3,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_4_width_0.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 1,
|
||||
textScale: 3,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_4_width_0.png'),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider value indicator large text scale and wide text',
|
||||
(WidgetTester tester) async {
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0,
|
||||
textScale: 3,
|
||||
decimalCount: 5,
|
||||
);
|
||||
|
||||
await _pressStartThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_start_text_scale_4_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 0.5,
|
||||
textScale: 3,
|
||||
decimalCount: 5,
|
||||
);
|
||||
|
||||
await _pressMiddleThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_middle_text_scale_4_width_5.png'),
|
||||
);
|
||||
|
||||
await _buildValueIndicatorStaticSlider(
|
||||
tester,
|
||||
value: 1,
|
||||
textScale: 3,
|
||||
decimalCount: 5,
|
||||
);
|
||||
|
||||
await _pressEndThumb(tester);
|
||||
|
||||
await expectLater(
|
||||
find.byType(MaterialApp),
|
||||
matchesGoldenFile('slider_end_text_scale_4_width_5.png'),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _pressStartThumb(WidgetTester tester) async {
|
||||
|
@ -204,9 +386,11 @@ Future<void> _buildValueIndicatorStaticSlider(
|
|||
required double value,
|
||||
double textScale = 1.0,
|
||||
int decimalCount = 0,
|
||||
bool useMaterial3 = false,
|
||||
}) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(useMaterial3: useMaterial3),
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
|
|
Loading…
Reference in a new issue