Commit 61c56ba1 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Make Slider take up whatever space it has. (#10132)

Previously it was (arbitrarily?) set to 144.0 pixels wide.
parent c67b46e3
...@@ -19,7 +19,8 @@ class _SliderDemoState extends State<SliderDemo> { ...@@ -19,7 +19,8 @@ class _SliderDemoState extends State<SliderDemo> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Scaffold( return new Scaffold(
appBar: new AppBar(title: const Text('Sliders')), appBar: new AppBar(title: const Text('Sliders')),
body: new Center( body: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: new Column( child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[ children: <Widget>[
......
...@@ -26,13 +26,17 @@ import 'typography.dart'; ...@@ -26,13 +26,17 @@ import 'typography.dart';
/// [max] is 50.0 and [divisions] is 5, then the slider can take on the values /// [max] is 50.0 and [divisions] is 5, then the slider can take on the values
/// discrete values 0.0, 10.0, 20.0, 30.0, 40.0, and 50.0. /// discrete values 0.0, 10.0, 20.0, 30.0, 40.0, and 50.0.
/// ///
/// The slider will be disabled if [onChanged] is null or if the range given by
/// [min]..[max] is empty (i.e. if [min] is equal to [max]).
///
/// The slider itself does not maintain any state. Instead, when the state of /// The slider itself does not maintain any state. Instead, when the state of
/// the slider changes, the widget calls the [onChanged] callback. Most widgets /// the slider changes, the widget calls the [onChanged] callback. Most widgets
/// that use a slider will listen for the [onChanged] callback and rebuild the /// that use a slider will listen for the [onChanged] callback and rebuild the
/// slider with a new [value] to update the visual appearance of the slider. /// slider with a new [value] to update the visual appearance of the slider.
/// ///
/// The slider will be disabled if [onChanged] is null or if the range given by /// By default, a slider will be as wide as possible, centered vertically. When
/// [min]..[max] is empty (i.e. if [min] is equal to [max]). /// given unbounded constraints, it will attempt to make the track 144 pixels
/// wide (with margins on each side) and will shrink-wrap vertically.
/// ///
/// Requires one of its ancestors to be a [Material] widget. /// Requires one of its ancestors to be a [Material] widget.
/// ///
...@@ -237,7 +241,11 @@ const double _kThumbRadius = 6.0; ...@@ -237,7 +241,11 @@ const double _kThumbRadius = 6.0;
const double _kActiveThumbRadius = 9.0; const double _kActiveThumbRadius = 9.0;
const double _kDisabledThumbRadius = 4.0; const double _kDisabledThumbRadius = 4.0;
const double _kReactionRadius = 16.0; const double _kReactionRadius = 16.0;
const double _kTrackWidth = 144.0; const double _kPreferredTrackWidth = 144.0;
const double _kMinimumTrackWidth = _kActiveThumbRadius; // biggest of the thumb radii
const double _kPreferredTotalWidth = _kPreferredTrackWidth + 2 * _kReactionRadius;
const double _kMinimumTotalWidth = _kMinimumTrackWidth + 2 * _kReactionRadius;
final Color _kInactiveTrackColor = Colors.grey.shade400; final Color _kInactiveTrackColor = Colors.grey.shade400;
final Color _kActiveTrackColor = Colors.grey; final Color _kActiveTrackColor = Colors.grey;
final Tween<double> _kReactionRadiusTween = new Tween<double>(begin: _kThumbRadius, end: _kReactionRadius); final Tween<double> _kReactionRadiusTween = new Tween<double>(begin: _kThumbRadius, end: _kReactionRadius);
...@@ -258,14 +266,11 @@ double _getAdditionalHeightForLabel(String label) { ...@@ -258,14 +266,11 @@ double _getAdditionalHeightForLabel(String label) {
return label == null ? 0.0 : _kLabelBalloonRadius * 2.0; return label == null ? 0.0 : _kLabelBalloonRadius * 2.0;
} }
BoxConstraints _getAdditionalConstraints(String label) { double _getPreferredTotalHeight(String label) {
return new BoxConstraints.tightFor( return 2 * _kReactionRadius + _getAdditionalHeightForLabel(label);
width: _kTrackWidth + 2 * _kReactionRadius,
height: 2 * _kReactionRadius + _getAdditionalHeightForLabel(label)
);
} }
class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandler { class _RenderSlider extends RenderBox implements SemanticsActionHandler {
_RenderSlider({ _RenderSlider({
@required double value, @required double value,
int divisions, int divisions,
...@@ -279,8 +284,7 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl ...@@ -279,8 +284,7 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl
_divisions = divisions, _divisions = divisions,
_activeColor = activeColor, _activeColor = activeColor,
_thumbOpenAtMin = thumbOpenAtMin, _thumbOpenAtMin = thumbOpenAtMin,
_textTheme = textTheme, _textTheme = textTheme {
super(additionalConstraints: _getAdditionalConstraints(label)) {
assert(value != null && value >= 0.0 && value <= 1.0); assert(value != null && value >= 0.0 && value <= 1.0);
this.label = label; this.label = label;
final GestureArenaTeam team = new GestureArenaTeam(); final GestureArenaTeam team = new GestureArenaTeam();
...@@ -335,7 +339,6 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl ...@@ -335,7 +339,6 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl
if (value == _label) if (value == _label)
return; return;
_label = value; _label = value;
additionalConstraints = _getAdditionalConstraints(_label);
if (value != null) { if (value != null) {
// TODO(abarth): Handle textScaleFactor. // TODO(abarth): Handle textScaleFactor.
// https://github.com/flutter/flutter/issues/5938 // https://github.com/flutter/flutter/issues/5938
...@@ -348,7 +351,7 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl ...@@ -348,7 +351,7 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl
} else { } else {
_labelPainter.text = null; _labelPainter.text = null;
} }
markNeedsPaint(); markNeedsLayout();
} }
Color get activeColor => _activeColor; Color get activeColor => _activeColor;
...@@ -448,11 +451,45 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl ...@@ -448,11 +451,45 @@ class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandl
} }
} }
@override
double computeMinIntrinsicWidth(double height) {
return _kMinimumTotalWidth;
}
@override
double computeMaxIntrinsicWidth(double height) {
// This doesn't quite match the definition of computeMaxIntrinsicWidth,
// but it seems within the spirit...
return _kPreferredTotalWidth;
}
@override
double computeMinIntrinsicHeight(double width) {
return _getPreferredTotalHeight(label);
}
@override
double computeMaxIntrinsicHeight(double width) {
return _getPreferredTotalHeight(label);
}
@override
bool get sizedByParent => true;
@override
void performResize() {
size = new Size(
constraints.hasBoundedWidth ? constraints.maxWidth : _kPreferredTotalWidth,
constraints.hasBoundedHeight ? constraints.maxHeight : _getPreferredTotalHeight(label),
);
}
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
final Canvas canvas = context.canvas; final Canvas canvas = context.canvas;
final double trackLength = _trackLength; final double trackLength = size.width - 2 * _kReactionRadius;
final bool enabled = isInteractive; final bool enabled = isInteractive;
final double value = _position.value; final double value = _position.value;
......
...@@ -49,17 +49,20 @@ void main() { ...@@ -49,17 +49,20 @@ void main() {
builder: (BuildContext context, StateSetter setState) { builder: (BuildContext context, StateSetter setState) {
return new Material( return new Material(
child: new Center( child: new Center(
child: new Slider( child: new SizedBox(
key: sliderKey, width: 144.0 + 2 * 16.0, // _kPreferredTotalWidth
min: 0.0, child: new Slider(
max: 100.0, key: sliderKey,
divisions: 10, min: 0.0,
value: value, max: 100.0,
onChanged: (double newValue) { divisions: 10,
setState(() { value: value,
value = newValue; onChanged: (double newValue) {
}); setState(() {
}, value = newValue;
});
},
),
), ),
), ),
); );
...@@ -188,4 +191,42 @@ void main() { ...@@ -188,4 +191,42 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('Slider sizing', (WidgetTester tester) async {
await tester.pumpWidget(const Material(
child: const Center(
child: const Slider(
value: 0.5,
onChanged: null,
),
),
));
expect(tester.renderObject<RenderBox>(find.byType(Slider)).size, const Size(800.0, 600.0));
await tester.pumpWidget(const Material(
child: const Center(
child: const IntrinsicWidth(
child: const Slider(
value: 0.5,
onChanged: null,
),
),
),
));
expect(tester.renderObject<RenderBox>(find.byType(Slider)).size, const Size(144.0 + 2.0 * 16.0, 600.0));
await tester.pumpWidget(const Material(
child: const Center(
child: const OverflowBox(
maxWidth: double.INFINITY,
maxHeight: double.INFINITY,
child: const Slider(
value: 0.5,
onChanged: null,
),
),
),
));
expect(tester.renderObject<RenderBox>(find.byType(Slider)).size, const Size(144.0 + 2.0 * 16.0, 32.0));
});
} }
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