Unverified Commit 10fe2056 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Add a slider demo, and a text theme for SliderThemeData (#15620)

This adds a slider demo with a custom theme to the gallery.

In the process of adding this, I decided to add a text theme to the SliderThemeData, since it's a pain to change the text style on the value indicator otherwise.
parent 7a285301
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SliderDemo extends StatefulWidget { class SliderDemo extends StatefulWidget {
...@@ -11,12 +13,124 @@ class SliderDemo extends StatefulWidget { ...@@ -11,12 +13,124 @@ class SliderDemo extends StatefulWidget {
_SliderDemoState createState() => new _SliderDemoState(); _SliderDemoState createState() => new _SliderDemoState();
} }
Path _triangle(double size, Offset thumbCenter, {bool invert: false}) {
final Path thumbPath = new Path();
final double height = math.sqrt(3.0) / 2.0;
final double halfSide = size / 2.0;
final double centerHeight = size * height / 3.0;
final double sign = invert ? -1.0 : 1.0;
thumbPath.moveTo(thumbCenter.dx - halfSide, thumbCenter.dy + sign * centerHeight);
thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2.0 * sign * centerHeight);
thumbPath.lineTo(thumbCenter.dx + halfSide, thumbCenter.dy + sign * centerHeight);
thumbPath.close();
return thumbPath;
}
class _CustomThumbShape extends SliderComponentShape {
static const double _thumbSize = 4.0;
static const double _disabledThumbSize = 3.0;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return isEnabled ? const Size.fromRadius(_thumbSize) : const Size.fromRadius(_disabledThumbSize);
}
static final Tween<double> sizeTween = new Tween<double>(
begin: _disabledThumbSize,
end: _thumbSize,
);
@override
void paint(
PaintingContext context,
Offset thumbCenter, {
Animation<double> activationAnimation,
Animation<double> enableAnimation,
bool isDiscrete,
TextPainter labelPainter,
RenderBox parentBox,
SliderThemeData sliderTheme,
TextDirection textDirection,
double value,
}) {
final Canvas canvas = context.canvas;
final ColorTween colorTween = new ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor,
);
final double size = _thumbSize * sizeTween.evaluate(enableAnimation);
final Path thumbPath = _triangle(size, thumbCenter);
canvas.drawPath(thumbPath, new Paint()..color = colorTween.evaluate(enableAnimation));
}
}
class _CustomValueIndicatorShape extends SliderComponentShape {
static const double _indicatorSize = 4.0;
static const double _disabledIndicatorSize = 3.0;
static const double _slideUpHeight = 40.0;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return new Size.fromRadius(isEnabled ? _indicatorSize : _disabledIndicatorSize);
}
static final Tween<double> sizeTween = new Tween<double>(
begin: _disabledIndicatorSize,
end: _indicatorSize,
);
@override
void paint(
PaintingContext context,
Offset thumbCenter, {
Animation<double> activationAnimation,
Animation<double> enableAnimation,
bool isDiscrete,
TextPainter labelPainter,
RenderBox parentBox,
SliderThemeData sliderTheme,
TextDirection textDirection,
double value,
}) {
final Canvas canvas = context.canvas;
final ColorTween enableColor = new ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.valueIndicatorColor,
);
final Tween<double> slideUpTween = new Tween<double>(
begin: 0.0,
end: _slideUpHeight,
);
final double size = _indicatorSize * sizeTween.evaluate(enableAnimation);
final Offset slideUpOffset = new Offset(0.0, -slideUpTween.evaluate(activationAnimation));
final Path thumbPath = _triangle(
size,
thumbCenter + slideUpOffset,
invert: true,
);
final Color paintColor = enableColor.evaluate(enableAnimation).withAlpha((255.0 * activationAnimation.value).round());
canvas.drawPath(
thumbPath,
new Paint()..color = paintColor,
);
canvas.drawLine(
thumbCenter,
thumbCenter + slideUpOffset,
new Paint()
..color = paintColor
..style = PaintingStyle.stroke
..strokeWidth = 2.0);
labelPainter.paint(canvas, thumbCenter + slideUpOffset + new Offset(-labelPainter.width / 2.0, -labelPainter.height - 4.0));
}
}
class _SliderDemoState extends State<SliderDemo> { class _SliderDemoState extends State<SliderDemo> {
double _value = 25.0; double _value = 25.0;
double _discreteValue = 20.0; double _discreteValue = 20.0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return new Scaffold( return new Scaffold(
appBar: new AppBar(title: const Text('Sliders')), appBar: new AppBar(title: const Text('Sliders')),
body: new Padding( body: new Padding(
...@@ -26,7 +140,7 @@ class _SliderDemoState extends State<SliderDemo> { ...@@ -26,7 +140,7 @@ class _SliderDemoState extends State<SliderDemo> {
children: <Widget>[ children: <Widget>[
new Column( new Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget> [ children: <Widget>[
new Slider( new Slider(
value: _value, value: _value,
min: 0.0, min: 0.0,
...@@ -35,21 +149,21 @@ class _SliderDemoState extends State<SliderDemo> { ...@@ -35,21 +149,21 @@ class _SliderDemoState extends State<SliderDemo> {
setState(() { setState(() {
_value = value; _value = value;
}); });
} },
), ),
const Text('Continuous'), const Text('Continuous'),
] ],
), ),
new Column( new Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: const <Widget> [ children: const <Widget>[
const Slider(value: 0.25, onChanged: null), const Slider(value: 0.25, onChanged: null),
const Text('Disabled'), const Text('Disabled'),
] ],
), ),
new Column( new Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget> [ children: <Widget>[
new Slider( new Slider(
value: _discreteValue, value: _discreteValue,
min: 0.0, min: 0.0,
...@@ -60,11 +174,43 @@ class _SliderDemoState extends State<SliderDemo> { ...@@ -60,11 +174,43 @@ class _SliderDemoState extends State<SliderDemo> {
setState(() { setState(() {
_discreteValue = value; _discreteValue = value;
}); });
} },
), ),
const Text('Discrete'), const Text('Discrete'),
], ],
), ),
new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new SliderTheme(
data: theme.sliderTheme.copyWith(
activeRailColor: Colors.deepPurple,
inactiveRailColor: Colors.black26,
activeTickMarkColor: Colors.white70,
inactiveTickMarkColor: Colors.black,
overlayColor: Colors.black12,
thumbColor: Colors.deepPurple,
valueIndicatorColor: Colors.deepPurpleAccent,
thumbShape: new _CustomThumbShape(),
valueIndicatorShape: new _CustomValueIndicatorShape(),
valueIndicatorTextStyle: theme.accentTextTheme.body2.copyWith(color: Colors.black87),
),
child: new Slider(
value: _discreteValue,
min: 0.0,
max: 100.0,
divisions: 5,
label: '${_discreteValue.round()}',
onChanged: (double value) {
setState(() {
_discreteValue = value;
});
},
),
),
const Text('Discrete with Custom Theme'),
],
),
], ],
), ),
), ),
......
...@@ -578,7 +578,10 @@ class _RenderSlider extends RenderBox { ...@@ -578,7 +578,10 @@ class _RenderSlider extends RenderBox {
void _updateLabelPainter() { void _updateLabelPainter() {
if (label != null) { if (label != null) {
_labelPainter _labelPainter
..text = new TextSpan(style: _theme.accentTextTheme.body2, text: label) ..text = new TextSpan(
style: _sliderTheme.valueIndicatorTextStyle,
text: label,
)
..textDirection = textDirection ..textDirection = textDirection
..textScaleFactor = _mediaQueryData.textScaleFactor ..textScaleFactor = _mediaQueryData.textScaleFactor
..layout(); ..layout();
...@@ -849,31 +852,31 @@ class _RenderSlider extends RenderBox { ...@@ -849,31 +852,31 @@ class _RenderSlider extends RenderBox {
_valueIndicatorAnimation.status != AnimationStatus.dismissed) { _valueIndicatorAnimation.status != AnimationStatus.dismissed) {
if (showValueIndicator) { if (showValueIndicator) {
_sliderTheme.valueIndicatorShape.paint( _sliderTheme.valueIndicatorShape.paint(
this,
context, context,
isDiscrete,
thumbCenter, thumbCenter,
_valueIndicatorAnimation, activationAnimation: _valueIndicatorAnimation,
_enableAnimation, enableAnimation: _enableAnimation,
_labelPainter, isDiscrete: isDiscrete,
_sliderTheme, labelPainter: _labelPainter,
_textDirection, parentBox: this,
value, sliderTheme: _sliderTheme,
textDirection: _textDirection,
value: _value,
); );
} }
} }
_sliderTheme.thumbShape.paint( _sliderTheme.thumbShape.paint(
this,
context, context,
isDiscrete,
thumbCenter, thumbCenter,
_overlayAnimation, activationAnimation: _valueIndicatorAnimation,
_enableAnimation, enableAnimation: _enableAnimation,
label != null ? _labelPainter : null, isDiscrete: isDiscrete,
_sliderTheme, labelPainter: _labelPainter,
_textDirection, parentBox: this,
value, sliderTheme: _sliderTheme,
textDirection: _textDirection,
value: _value,
); );
} }
......
...@@ -166,6 +166,7 @@ class ThemeData extends Diagnosticable { ...@@ -166,6 +166,7 @@ class ThemeData extends Diagnosticable {
primaryColor: primaryColor, primaryColor: primaryColor,
primaryColorLight: primaryColorLight, primaryColorLight: primaryColorLight,
primaryColorDark: primaryColorDark, primaryColorDark: primaryColorDark,
valueIndicatorTextStyle: accentTextTheme.body2,
); );
return new ThemeData.raw( return new ThemeData.raw(
brightness: brightness, brightness: brightness,
......
...@@ -25,17 +25,17 @@ class LoggingThumbShape extends SliderComponentShape { ...@@ -25,17 +25,17 @@ class LoggingThumbShape extends SliderComponentShape {
@override @override
void paint( void paint(
RenderBox parentBox,
PaintingContext context, PaintingContext context,
bool isDiscrete, Offset thumbCenter, {
Offset thumbCenter,
Animation<double> activationAnimation, Animation<double> activationAnimation,
Animation<double> enableAnimation, Animation<double> enableAnimation,
bool isDiscrete,
TextPainter labelPainter, TextPainter labelPainter,
RenderBox parentBox,
SliderThemeData sliderTheme, SliderThemeData sliderTheme,
TextDirection textDirection, TextDirection textDirection,
double value, double value,
) { }) {
log.add(thumbCenter); log.add(thumbCenter);
final Paint thumbPaint = new Paint()..color = Colors.red; final Paint thumbPaint = new Paint()..color = Colors.red;
context.canvas.drawCircle(thumbCenter, 5.0, thumbPaint); context.canvas.drawCircle(thumbCenter, 5.0, thumbPaint);
......
...@@ -104,11 +104,13 @@ void main() { ...@@ -104,11 +104,13 @@ void main() {
const Color customColor1 = const Color(0xcafefeed); const Color customColor1 = const Color(0xcafefeed);
const Color customColor2 = const Color(0xdeadbeef); const Color customColor2 = const Color(0xdeadbeef);
const Color customColor3 = const Color(0xdecaface); const Color customColor3 = const Color(0xdecaface);
const Color customColor4 = const Color(0xfeedcafe);
final SliderThemeData sliderTheme = new SliderThemeData.fromPrimaryColors( final SliderThemeData sliderTheme = new SliderThemeData.fromPrimaryColors(
primaryColor: customColor1, primaryColor: customColor1,
primaryColorDark: customColor2, primaryColorDark: customColor2,
primaryColorLight: customColor3, primaryColorLight: customColor3,
valueIndicatorTextStyle: new ThemeData.fallback().accentTextTheme.body2.copyWith(color: customColor4),
); );
expect(sliderTheme.activeRailColor, equals(customColor1.withAlpha(0xff))); expect(sliderTheme.activeRailColor, equals(customColor1.withAlpha(0xff)));
...@@ -126,6 +128,7 @@ void main() { ...@@ -126,6 +128,7 @@ void main() {
expect(sliderTheme.thumbShape, equals(const isInstanceOf<RoundSliderThumbShape>())); expect(sliderTheme.thumbShape, equals(const isInstanceOf<RoundSliderThumbShape>()));
expect(sliderTheme.valueIndicatorShape, equals(const isInstanceOf<PaddleSliderValueIndicatorShape>())); expect(sliderTheme.valueIndicatorShape, equals(const isInstanceOf<PaddleSliderValueIndicatorShape>()));
expect(sliderTheme.showValueIndicator, equals(ShowValueIndicator.onlyForDiscrete)); expect(sliderTheme.showValueIndicator, equals(ShowValueIndicator.onlyForDiscrete));
expect(sliderTheme.valueIndicatorTextStyle.color, equals(customColor4));
}); });
testWidgets('SliderThemeData lerps correctly', (WidgetTester tester) async { testWidgets('SliderThemeData lerps correctly', (WidgetTester tester) async {
...@@ -133,11 +136,13 @@ void main() { ...@@ -133,11 +136,13 @@ void main() {
primaryColor: Colors.black, primaryColor: Colors.black,
primaryColorDark: Colors.black, primaryColorDark: Colors.black,
primaryColorLight: Colors.black, primaryColorLight: Colors.black,
valueIndicatorTextStyle: new ThemeData.fallback().accentTextTheme.body2.copyWith(color: Colors.black),
); );
final SliderThemeData sliderThemeWhite = new SliderThemeData.fromPrimaryColors( final SliderThemeData sliderThemeWhite = new SliderThemeData.fromPrimaryColors(
primaryColor: Colors.white, primaryColor: Colors.white,
primaryColorDark: Colors.white, primaryColorDark: Colors.white,
primaryColorLight: Colors.white, primaryColorLight: Colors.white,
valueIndicatorTextStyle: new ThemeData.fallback().accentTextTheme.body2.copyWith(color: Colors.white),
); );
final SliderThemeData lerp = SliderThemeData.lerp(sliderThemeBlack, sliderThemeWhite, 0.5); final SliderThemeData lerp = SliderThemeData.lerp(sliderThemeBlack, sliderThemeWhite, 0.5);
const Color middleGrey = const Color(0xff7f7f7f); const Color middleGrey = const Color(0xff7f7f7f);
...@@ -153,6 +158,7 @@ void main() { ...@@ -153,6 +158,7 @@ void main() {
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(0x29)));
expect(lerp.valueIndicatorColor, equals(middleGrey.withAlpha(0xff))); expect(lerp.valueIndicatorColor, equals(middleGrey.withAlpha(0xff)));
expect(lerp.valueIndicatorTextStyle.color, equals(middleGrey.withAlpha(0xff)));
}); });
testWidgets('Default slider thumb shape draws correctly', (WidgetTester tester) async { testWidgets('Default slider thumb shape draws correctly', (WidgetTester tester) async {
......
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