diff --git a/packages/flutter/lib/src/material/slider_theme.dart b/packages/flutter/lib/src/material/slider_theme.dart index 9497aa8b8ba..1603e5b77a6 100644 --- a/packages/flutter/lib/src/material/slider_theme.dart +++ b/packages/flutter/lib/src/material/slider_theme.dart @@ -55,10 +55,10 @@ class SliderTheme extends InheritedWidget { /// @override /// State createState() => new LaunchState(); /// } - /// + /// /// class LaunchState extends State { /// double _rocketThrust; - /// + /// /// @override /// Widget build(BuildContext context) { /// return new SliderTheme( @@ -621,6 +621,11 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { static const double _thirtyDegrees = math.pi / 6.0; static const Size _preferredSize = const Size.fromHeight(_distanceBetweenTopBottomCenters + _topLobeRadius + _bottomLobeRadius); + // Set to true if you want a rectangle to be drawn around the label bubble. + // This helps with building tests that check that the label draws in the right + // place (because it prints the rect in the failed test output). It should not + // be checked in while set to "true". + static const bool _debuggingLabelLocation = false; static Path _bottomLobePath; // Initialized by _generateBottomLobe static Offset _bottomLobeEnd; // Initialized by _generateBottomLobe @@ -767,6 +772,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { final Path path = new Path(); final Offset bottomLobeEnd = _addBottomLobe(path); + // The base of the triangle between the top lobe center and the centers of // the two top neck arcs. final double neckTriangleBase = _topNeckRadius - bottomLobeEnd.dx; @@ -789,31 +795,54 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ); final double leftNeckArcAngle = _ninetyDegrees - leftTheta; final double rightNeckArcAngle = math.pi + _ninetyDegrees - rightTheta; + // 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 + // factor of the value indicator. + final double neckStretchBaseline = bottomLobeEnd.dy - math.max(neckLeftCenter.dy, neckRightCenter.dy); + final double t = math.pow(inverseTextScale, 3.0); + final double stretch = (neckStretchBaseline * t).clamp(0.0, 10.0 * neckStretchBaseline); + final Offset neckStretch = new Offset(0.0, neckStretchBaseline - stretch); + + assert(!_debuggingLabelLocation || () { + final Offset leftCenter = _topLobeCenter - new Offset(leftWidthNeeded, 0.0) + neckStretch; + final Offset rightCenter = _topLobeCenter + new Offset(rightWidthNeeded, 0.0) + neckStretch; + final Rect valueRect = new Rect.fromLTRB( + leftCenter.dx - _topLobeRadius, + leftCenter.dy - _topLobeRadius, + rightCenter.dx + _topLobeRadius, + rightCenter.dy + _topLobeRadius, + ); + final Paint outlinePaint = new Paint() + ..color = const Color(0xffff0000) + ..style = PaintingStyle.stroke..strokeWidth = 1.0; + canvas.drawRect(valueRect, outlinePaint); + return true; + }()); _addArc( path, - neckLeftCenter, + neckLeftCenter + neckStretch, _topNeckRadius, 0.0, -leftNeckArcAngle, ); _addArc( path, - _topLobeCenter - new Offset(leftWidthNeeded, 0.0), + _topLobeCenter - new Offset(leftWidthNeeded, 0.0) + neckStretch, _topLobeRadius, _ninetyDegrees + leftTheta, _twoSeventyDegrees, ); _addArc( path, - _topLobeCenter + new Offset(rightWidthNeeded, 0.0), + _topLobeCenter + new Offset(rightWidthNeeded, 0.0) + neckStretch, _topLobeRadius, _twoSeventyDegrees, _twoSeventyDegrees + math.pi - rightTheta, ); _addArc( path, - neckRightCenter, + neckRightCenter + neckStretch, _topNeckRadius, rightNeckArcAngle, math.pi, @@ -822,7 +851,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { // Draw the label. canvas.save(); - canvas.translate(shift, -_distanceBetweenTopBottomCenters); + canvas.translate(shift, -_distanceBetweenTopBottomCenters + neckStretch.dy); canvas.scale(inverseTextScale, inverseTextScale); labelPainter.paint(canvas, Offset.zero - new Offset(labelHalfWidth, labelPainter.height / 2.0)); canvas.restore(); diff --git a/packages/flutter/test/material/slider_theme_test.dart b/packages/flutter/test/material/slider_theme_test.dart index 7e220624de3..79f53a67306 100644 --- a/packages/flutter/test/material/slider_theme_test.dart +++ b/packages/flutter/test/material/slider_theme_test.dart @@ -195,11 +195,11 @@ void main() { expect(sliderBox, paints..circle(color: sliderTheme.thumbColor, radius: 6.0)); await tester.pumpWidget(buildApp(enabled: false)); - await tester.pump(const Duration(milliseconds: 500)); // wait for disable animation + await tester.pumpAndSettle(); // wait for disable animation expect(sliderBox, paints..circle(color: sliderTheme.disabledThumbColor, radius: 4.0)); await tester.pumpWidget(buildApp(divisions: 3)); - await tester.pump(const Duration(milliseconds: 500)); // wait for disable animation + await tester.pumpAndSettle(); // wait for disable animation expect( sliderBox, paints @@ -210,7 +210,7 @@ void main() { ..circle(color: sliderTheme.thumbColor, radius: 6.0)); await tester.pumpWidget(buildApp(divisions: 3, enabled: false)); - await tester.pump(const Duration(milliseconds: 500)); // wait for disable animation + await tester.pumpAndSettle(); // wait for disable animation expect( sliderBox, paints @@ -226,11 +226,11 @@ void main() { primarySwatch: Colors.blue, ); final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500, showValueIndicator: ShowValueIndicator.always); - Widget buildApp(String value, {double sliderValue = 0.5}) { + Widget buildApp(String value, {double sliderValue = 0.5, double textScale = 1.0}) { return new Directionality( textDirection: TextDirection.ltr, child: new MediaQuery( - data: new MediaQueryData.fromWindow(window), + data: new MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScale), child: new Material( child: new Row( children: [ @@ -258,9 +258,8 @@ void main() { Offset center = tester.getCenter(find.byType(Slider)); TestGesture gesture = await tester.startGesture(center); - await tester.pump(); // Wait for value indicator animation to finish. - await tester.pump(const Duration(milliseconds: 500)); + await tester.pumpAndSettle(); expect( sliderBox, paints @@ -280,9 +279,8 @@ void main() { await tester.pumpWidget(buildApp('1000')); center = tester.getCenter(find.byType(Slider)); gesture = await tester.startGesture(center); - await tester.pump(); // Wait for value indicator animation to finish. - await tester.pump(const Duration(milliseconds: 500)); + await tester.pumpAndSettle(); expect( sliderBox, paints @@ -301,9 +299,8 @@ void main() { await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0)); center = tester.getCenter(find.byType(Slider)); gesture = await tester.startGesture(center); - await tester.pump(); // Wait for value indicator animation to finish. - await tester.pump(const Duration(milliseconds: 500)); + await tester.pumpAndSettle(); expect( sliderBox, paints @@ -322,9 +319,8 @@ void main() { await tester.pumpWidget(buildApp('1000000', sliderValue: 1.0)); center = tester.getCenter(find.byType(Slider)); gesture = await tester.startGesture(center); - await tester.pump(); // Wait for value indicator animation to finish. - await tester.pump(const Duration(milliseconds: 500)); + await tester.pumpAndSettle(); expect( sliderBox, paints @@ -338,5 +334,55 @@ void main() { excludes: [const Offset(16.1, -40.0), const Offset(-98.1, -40.0)], )); await gesture.up(); + + // Test that the neck stretches when the text scale gets smaller. + await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0, textScale: 0.5)); + center = tester.getCenter(find.byType(Slider)); + gesture = await tester.startGesture(center); + // Wait for value indicator animation to finish. + await tester.pumpAndSettle(); + expect( + sliderBox, + paints + ..path( + color: sliderTheme.valueIndicatorColor, + includes: [ + const Offset(0.0, -49.0), + const Offset(90.0, -49.0), + const Offset(-24.0, -49.0), + ], + excludes: [ + const Offset(98.0, -32.0), // inside full size, outside small + const Offset(-16.0, -32.0), // inside full size, outside small + const Offset(90.1, -49.0), + const Offset(-24.1, -49.0), + ], + )); + await gesture.up(); + + // Test that the neck shrinks when the text scale gets larger. + await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0, textScale: 2.5)); + center = tester.getCenter(find.byType(Slider)); + gesture = await tester.startGesture(center); + // Wait for value indicator animation to finish. + await tester.pumpAndSettle(); + expect( + sliderBox, + paints + ..path( + color: sliderTheme.valueIndicatorColor, + includes: [ + const Offset(0.0, -38.8), + const Offset(98.0, -38.8), + const Offset(-16.0, -38.8), + const Offset(10.0, -23.0), // Inside large, outside scale=1.0 + const Offset(-4.0, -23.0), // Inside large, outside scale=1.0 + ], + excludes: [ + const Offset(98.5, -38.8), + const Offset(-16.1, -38.8), + ], + )); + await gesture.up(); }); }