Unverified Commit 04bd8779 authored by Anthony's avatar Anthony Committed by GitHub

[Material] Update slider gallery demo, including range slider (#34597)

Update the flutter_gallery Slider demo by aligning the text field in the first slider, adding range slider examples (including custom range thumbs), and separating single slider and range slider examples with tabs.
parent a276ab3d
...@@ -15,19 +15,95 @@ class SliderDemo extends StatefulWidget { ...@@ -15,19 +15,95 @@ class SliderDemo extends StatefulWidget {
_SliderDemoState createState() => _SliderDemoState(); _SliderDemoState createState() => _SliderDemoState();
} }
Path _triangle(double size, Offset thumbCenter, {bool invert = false}) { Path _downTriangle(double size, Offset thumbCenter, { bool invert = false }) {
final Path thumbPath = Path(); final Path thumbPath = Path();
final double height = math.sqrt(3.0) / 2.0; final double height = math.sqrt(3.0) / 2.0;
final double halfSide = size / 2.0;
final double centerHeight = size * height / 3.0; final double centerHeight = size * height / 3.0;
final double halfSize = size / 2.0;
final double sign = invert ? -1.0 : 1.0; final double sign = invert ? -1.0 : 1.0;
thumbPath.moveTo(thumbCenter.dx - halfSide, thumbCenter.dy + sign * centerHeight); thumbPath.moveTo(thumbCenter.dx - halfSize, thumbCenter.dy + sign * centerHeight);
thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2.0 * sign * centerHeight); thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2.0 * sign * centerHeight);
thumbPath.lineTo(thumbCenter.dx + halfSide, thumbCenter.dy + sign * centerHeight); thumbPath.lineTo(thumbCenter.dx + halfSize, thumbCenter.dy + sign * centerHeight);
thumbPath.close(); thumbPath.close();
return thumbPath; return thumbPath;
} }
Path _rightTriangle(double size, Offset thumbCenter, { bool invert = false }) {
final Path thumbPath = Path();
final double halfSize = size / 2.0;
final double sign = invert ? -1.0 : 1.0;
thumbPath.moveTo(thumbCenter.dx + halfSize * sign, thumbCenter.dy);
thumbPath.lineTo(thumbCenter.dx - halfSize * sign, thumbCenter.dy - size);
thumbPath.lineTo(thumbCenter.dx - halfSize * sign, thumbCenter.dy + size);
thumbPath.close();
return thumbPath;
}
Path _upTriangle(double size, Offset thumbCenter) => _downTriangle(size, thumbCenter, invert: true);
Path _leftTriangle(double size, Offset thumbCenter) => _rightTriangle(size, thumbCenter, invert: true);
class _CustomRangeThumbShape extends RangeSliderThumbShape {
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 Animatable<double> sizeTween = Tween<double>(
begin: _disabledThumbSize,
end: _thumbSize,
);
@override
void paint(
PaintingContext context,
Offset center, {
@required Animation<double> activationAnimation,
@required Animation<double> enableAnimation,
bool isDiscrete = false,
bool isEnabled = false,
bool isOnTop,
@required SliderThemeData sliderTheme,
TextDirection textDirection,
Thumb thumb,
}) {
final Canvas canvas = context.canvas;
final ColorTween colorTween = ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor,
);
final double size = _thumbSize * sizeTween.evaluate(enableAnimation);
Path thumbPath;
switch (textDirection) {
case TextDirection.rtl:
switch (thumb) {
case Thumb.start:
thumbPath = _rightTriangle(size, center);
break;
case Thumb.end:
thumbPath = _leftTriangle(size, center);
break;
}
break;
case TextDirection.ltr:
switch (thumb) {
case Thumb.start:
thumbPath = _leftTriangle(size, center);
break;
case Thumb.end:
thumbPath = _rightTriangle(size, center);
break;
}
break;
}
canvas.drawPath(thumbPath, Paint()..color = colorTween.evaluate(enableAnimation));
}
}
class _CustomThumbShape extends SliderComponentShape { class _CustomThumbShape extends SliderComponentShape {
static const double _thumbSize = 4.0; static const double _thumbSize = 4.0;
static const double _disabledThumbSize = 3.0; static const double _disabledThumbSize = 3.0;
...@@ -61,7 +137,7 @@ class _CustomThumbShape extends SliderComponentShape { ...@@ -61,7 +137,7 @@ class _CustomThumbShape extends SliderComponentShape {
end: sliderTheme.thumbColor, end: sliderTheme.thumbColor,
); );
final double size = _thumbSize * sizeTween.evaluate(enableAnimation); final double size = _thumbSize * sizeTween.evaluate(enableAnimation);
final Path thumbPath = _triangle(size, thumbCenter); final Path thumbPath = _downTriangle(size, thumbCenter);
canvas.drawPath(thumbPath, Paint()..color = colorTween.evaluate(enableAnimation)); canvas.drawPath(thumbPath, Paint()..color = colorTween.evaluate(enableAnimation));
} }
} }
...@@ -105,11 +181,7 @@ class _CustomValueIndicatorShape extends SliderComponentShape { ...@@ -105,11 +181,7 @@ class _CustomValueIndicatorShape extends SliderComponentShape {
); );
final double size = _indicatorSize * sizeTween.evaluate(enableAnimation); final double size = _indicatorSize * sizeTween.evaluate(enableAnimation);
final Offset slideUpOffset = Offset(0.0, -slideUpTween.evaluate(activationAnimation)); final Offset slideUpOffset = Offset(0.0, -slideUpTween.evaluate(activationAnimation));
final Path thumbPath = _triangle( final Path thumbPath = _upTriangle(size, thumbCenter + slideUpOffset);
size,
thumbCenter + slideUpOffset,
invert: true,
);
final Color paintColor = enableColor.evaluate(enableAnimation).withAlpha((255.0 * activationAnimation.value).round()); final Color paintColor = enableColor.evaluate(enableAnimation).withAlpha((255.0 * activationAnimation.value).round());
canvas.drawPath( canvas.drawPath(
thumbPath, thumbPath,
...@@ -127,18 +199,46 @@ class _CustomValueIndicatorShape extends SliderComponentShape { ...@@ -127,18 +199,46 @@ class _CustomValueIndicatorShape extends SliderComponentShape {
} }
class _SliderDemoState extends State<SliderDemo> { class _SliderDemoState extends State<SliderDemo> {
double _value = 25.0; @override
Widget build(BuildContext context) {
final List<ComponentDemoTabData> demos = <ComponentDemoTabData>[
ComponentDemoTabData(
tabName: 'SINGLE',
description: 'Sliders containing 1 thumb',
demoWidget: _Sliders(),
documentationUrl: 'https://docs.flutter.io/flutter/material/Slider-class.html',
),
ComponentDemoTabData(
tabName: 'RANGE',
description: 'Sliders containing 2 thumbs',
demoWidget: _RangeSliders(),
documentationUrl: 'https://docs.flutter.io/flutter/material/RangeSlider-class.html',
),
];
return TabbedComponentDemoScaffold(
title: 'Sliders',
demos: demos,
isScrollable: false,
showExampleCodeAction: false,
);
}
}
class _Sliders extends StatefulWidget {
@override
_SlidersState createState() => _SlidersState();
}
class _SlidersState extends State<_Sliders> {
double _continuousValue = 25.0;
double _discreteValue = 20.0; double _discreteValue = 20.0;
double _discreteCustomValue = 25.0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
return Scaffold( return Padding(
appBar: AppBar(
title: const Text('Sliders'),
actions: <Widget>[MaterialDemoDocumentationButton(SliderDemo.routeName)],
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0), padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
...@@ -146,58 +246,37 @@ class _SliderDemoState extends State<SliderDemo> { ...@@ -146,58 +246,37 @@ class _SliderDemoState extends State<SliderDemo> {
Column( Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
Slider.adaptive(
value: _value,
min: 0.0,
max: 100.0,
onChanged: (double value) {
setState(() {
_value = value;
});
},
),
const Text('Continuous'),
],
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Slider.adaptive(
value: _value,
min: 0.0,
max: 100.0,
onChanged: (double value) {
setState(() {
_value = value;
});
},
),
),
Semantics( Semantics(
label: 'Editable numerical value', label: 'Editable numerical value',
child: Container( child: SizedBox(
width: 48, width: 64,
height: 48, height: 48,
child: TextField( child: TextField(
textAlign: TextAlign.center,
onSubmitted: (String value) { onSubmitted: (String value) {
final double newValue = double.tryParse(value); final double newValue = double.tryParse(value);
if (newValue != null && newValue != _value) { if (newValue != null && newValue != _continuousValue) {
setState(() { setState(() {
_value = newValue.clamp(0, 100); _continuousValue = newValue.clamp(0, 100);
}); });
} }
}, },
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
controller: TextEditingController( controller: TextEditingController(
text: _value.toStringAsFixed(0), text: _continuousValue.toStringAsFixed(0),
), ),
), ),
), ),
), ),
], Slider.adaptive(
value: _continuousValue,
min: 0.0,
max: 100.0,
onChanged: (double value) {
setState(() {
_continuousValue = value;
});
},
), ),
const Text('Continuous with Editable Numerical Value'), const Text('Continuous with Editable Numerical Value'),
], ],
...@@ -233,26 +312,26 @@ class _SliderDemoState extends State<SliderDemo> { ...@@ -233,26 +312,26 @@ class _SliderDemoState extends State<SliderDemo> {
SliderTheme( SliderTheme(
data: theme.sliderTheme.copyWith( data: theme.sliderTheme.copyWith(
activeTrackColor: Colors.deepPurple, activeTrackColor: Colors.deepPurple,
inactiveTrackColor: Colors.black26, inactiveTrackColor: theme.colorScheme.onSurface.withOpacity(0.5),
activeTickMarkColor: Colors.white70, activeTickMarkColor: theme.colorScheme.onSurface.withOpacity(0.7),
inactiveTickMarkColor: Colors.black, inactiveTickMarkColor: theme.colorScheme.surface.withOpacity(0.7),
overlayColor: Colors.black12, overlayColor: theme.colorScheme.onSurface.withOpacity(0.12),
thumbColor: Colors.deepPurple, thumbColor: Colors.deepPurple,
valueIndicatorColor: Colors.deepPurpleAccent, valueIndicatorColor: Colors.deepPurpleAccent,
thumbShape: _CustomThumbShape(), thumbShape: _CustomThumbShape(),
valueIndicatorShape: _CustomValueIndicatorShape(), valueIndicatorShape: _CustomValueIndicatorShape(),
valueIndicatorTextStyle: theme.accentTextTheme.body2.copyWith(color: Colors.black87), valueIndicatorTextStyle: theme.accentTextTheme.body2.copyWith(color: theme.colorScheme.onSurface),
), ),
child: Slider( child: Slider(
value: _discreteValue, value: _discreteCustomValue,
min: 0.0, min: 0.0,
max: 200.0, max: 200.0,
divisions: 5, divisions: 5,
semanticFormatterCallback: (double value) => value.round().toString(), semanticFormatterCallback: (double value) => value.round().toString(),
label: '${_discreteValue.round()}', label: '${_discreteCustomValue.round()}',
onChanged: (double value) { onChanged: (double value) {
setState(() { setState(() {
_discreteValue = value; _discreteCustomValue = value;
}); });
}, },
), ),
...@@ -262,7 +341,101 @@ class _SliderDemoState extends State<SliderDemo> { ...@@ -262,7 +341,101 @@ class _SliderDemoState extends State<SliderDemo> {
), ),
], ],
), ),
);
}
}
class _RangeSliders extends StatefulWidget {
@override
_RangeSlidersState createState() => _RangeSlidersState();
}
class _RangeSlidersState extends State<_RangeSliders> {
RangeValues _continuousValues = const RangeValues(25.0, 75.0);
RangeValues _discreteValues = const RangeValues(40.0, 120.0);
RangeValues _discreteCustomValues = const RangeValues(40.0, 160.0);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RangeSlider(
values: _continuousValues,
min: 0.0,
max: 100.0,
onChanged: (RangeValues values) {
setState(() {
_continuousValues = values;
});
},
),
const Text('Continuous'),
],
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RangeSlider(values: const RangeValues(0.25, 0.75), onChanged: null),
const Text('Disabled'),
],
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RangeSlider(
values: _discreteValues,
min: 0.0,
max: 200.0,
divisions: 5,
labels: RangeLabels('${_discreteValues.start.round()}', '${_discreteValues.end.round()}'),
onChanged: (RangeValues values) {
setState(() {
_discreteValues = values;
});
},
),
const Text('Discrete'),
],
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SliderTheme(
data: SliderThemeData(
activeTrackColor: Colors.deepPurple,
inactiveTrackColor: Colors.black26,
activeTickMarkColor: Colors.white70,
inactiveTickMarkColor: Colors.black,
overlayColor: Colors.black12,
thumbColor: Colors.deepPurple,
rangeThumbShape: _CustomRangeThumbShape(),
showValueIndicator: ShowValueIndicator.never,
),
child: RangeSlider(
values: _discreteCustomValues,
min: 0.0,
max: 200.0,
divisions: 5,
labels: RangeLabels('${_discreteCustomValues.start.round()}', '${_discreteCustomValues.end.round()}'),
onChanged: (RangeValues values) {
setState(() {
_discreteCustomValues = values;
});
},
),
),
const Text('Discrete with Custom Theme'),
],
),
],
), ),
); );
} }
} }
...@@ -46,11 +46,15 @@ class TabbedComponentDemoScaffold extends StatelessWidget { ...@@ -46,11 +46,15 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
this.title, this.title,
this.demos, this.demos,
this.actions, this.actions,
this.isScrollable = true,
this.showExampleCodeAction = true,
}); });
final List<ComponentDemoTabData> demos; final List<ComponentDemoTabData> demos;
final String title; final String title;
final List<Widget> actions; final List<Widget> actions;
final bool isScrollable;
final bool showExampleCodeAction;
void _showExampleCode(BuildContext context) { void _showExampleCode(BuildContext context) {
final String tag = demos[DefaultTabController.of(context).index].exampleCodeTag; final String tag = demos[DefaultTabController.of(context).index].exampleCodeTag;
...@@ -93,8 +97,7 @@ class TabbedComponentDemoScaffold extends StatelessWidget { ...@@ -93,8 +97,7 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(title), title: Text(title),
actions: (actions ?? <Widget>[])..addAll( actions: (actions ?? <Widget>[])..add(
<Widget>[
Builder( Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
return IconButton( return IconButton(
...@@ -103,6 +106,7 @@ class TabbedComponentDemoScaffold extends StatelessWidget { ...@@ -103,6 +106,7 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
); );
}, },
), ),
)..addAll(showExampleCodeAction ? <Widget>[
Builder( Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
return IconButton( return IconButton(
...@@ -112,10 +116,9 @@ class TabbedComponentDemoScaffold extends StatelessWidget { ...@@ -112,10 +116,9 @@ class TabbedComponentDemoScaffold extends StatelessWidget {
); );
}, },
), ),
], ] : <Widget>[]),
),
bottom: TabBar( bottom: TabBar(
isScrollable: true, isScrollable: isScrollable,
tabs: demos.map<Widget>((ComponentDemoTabData data) => Tab(text: data.tabName)).toList(), tabs: demos.map<Widget>((ComponentDemoTabData data) => Tab(text: data.tabName)).toList(),
), ),
), ),
......
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