Make Slider take up whatever space it has. (#10132)
Previously it was (arbitrarily?) set to 144.0 pixels wide.
diff --git a/examples/flutter_gallery/lib/demo/material/slider_demo.dart b/examples/flutter_gallery/lib/demo/material/slider_demo.dart
index 7441d67..4bc9c29 100644
--- a/examples/flutter_gallery/lib/demo/material/slider_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/slider_demo.dart
@@ -19,7 +19,8 @@
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: const Text('Sliders')),
- body: new Center(
+ body: new Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart
index 584313f..baa9f7e 100644
--- a/packages/flutter/lib/src/material/slider.dart
+++ b/packages/flutter/lib/src/material/slider.dart
@@ -26,13 +26,17 @@
/// [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.
///
+/// 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 changes, the widget calls the [onChanged] callback. Most widgets
/// 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.
///
-/// 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]).
+/// By default, a slider will be as wide as possible, centered vertically. When
+/// 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.
///
@@ -237,7 +241,11 @@
const double _kActiveThumbRadius = 9.0;
const double _kDisabledThumbRadius = 4.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 _kActiveTrackColor = Colors.grey;
final Tween<double> _kReactionRadiusTween = new Tween<double>(begin: _kThumbRadius, end: _kReactionRadius);
@@ -258,14 +266,11 @@
return label == null ? 0.0 : _kLabelBalloonRadius * 2.0;
}
-BoxConstraints _getAdditionalConstraints(String label) {
- return new BoxConstraints.tightFor(
- width: _kTrackWidth + 2 * _kReactionRadius,
- height: 2 * _kReactionRadius + _getAdditionalHeightForLabel(label)
- );
+double _getPreferredTotalHeight(String label) {
+ return 2 * _kReactionRadius + _getAdditionalHeightForLabel(label);
}
-class _RenderSlider extends RenderConstrainedBox implements SemanticsActionHandler {
+class _RenderSlider extends RenderBox implements SemanticsActionHandler {
_RenderSlider({
@required double value,
int divisions,
@@ -279,8 +284,7 @@
_divisions = divisions,
_activeColor = activeColor,
_thumbOpenAtMin = thumbOpenAtMin,
- _textTheme = textTheme,
- super(additionalConstraints: _getAdditionalConstraints(label)) {
+ _textTheme = textTheme {
assert(value != null && value >= 0.0 && value <= 1.0);
this.label = label;
final GestureArenaTeam team = new GestureArenaTeam();
@@ -335,7 +339,6 @@
if (value == _label)
return;
_label = value;
- additionalConstraints = _getAdditionalConstraints(_label);
if (value != null) {
// TODO(abarth): Handle textScaleFactor.
// https://github.com/flutter/flutter/issues/5938
@@ -348,7 +351,7 @@
} else {
_labelPainter.text = null;
}
- markNeedsPaint();
+ markNeedsLayout();
}
Color get activeColor => _activeColor;
@@ -448,11 +451,45 @@
}
}
+
+ @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
void paint(PaintingContext context, Offset offset) {
final Canvas canvas = context.canvas;
- final double trackLength = _trackLength;
+ final double trackLength = size.width - 2 * _kReactionRadius;
final bool enabled = isInteractive;
final double value = _position.value;
diff --git a/packages/flutter/test/material/slider_test.dart b/packages/flutter/test/material/slider_test.dart
index 56c1c9e..3942e40 100644
--- a/packages/flutter/test/material/slider_test.dart
+++ b/packages/flutter/test/material/slider_test.dart
@@ -49,17 +49,20 @@
builder: (BuildContext context, StateSetter setState) {
return new Material(
child: new Center(
- child: new Slider(
- key: sliderKey,
- min: 0.0,
- max: 100.0,
- divisions: 10,
- value: value,
- onChanged: (double newValue) {
- setState(() {
- value = newValue;
- });
- },
+ child: new SizedBox(
+ width: 144.0 + 2 * 16.0, // _kPreferredTotalWidth
+ child: new Slider(
+ key: sliderKey,
+ min: 0.0,
+ max: 100.0,
+ divisions: 10,
+ value: value,
+ onChanged: (double newValue) {
+ setState(() {
+ value = newValue;
+ });
+ },
+ ),
),
),
);
@@ -188,4 +191,42 @@
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));
+ });
}