Commit 767ce826 authored by Adam Barth's avatar Adam Barth

Add support for discrete material sliders

Fixes #1541
parent daa0d2df
......@@ -11,53 +11,44 @@ class SliderDemo extends StatefulWidget {
class _SliderDemoState extends State<SliderDemo> {
double _value = 25.0;
double _discreteValue = 20.0;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Sliders")),
body: new Block(children: <Widget>[
new Container(
height: 100.0,
child: new Center(
child: new Row(
children: <Widget>[
new Slider(
value: _value,
min: 0.0,
max: 100.0,
onChanged: (double value) {
setState(() {
_value = value;
});
}
),
new Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Text(_value.round().toString().padLeft(3, '0'))
),
],
mainAxisAlignment: MainAxisAlignment.collapse
appBar: new AppBar(title: new Text('Sliders')),
body: new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new Center(
child: new Slider(
value: _value,
min: 0.0,
max: 100.0,
onChanged: (double value) {
setState(() {
_value = value;
});
}
)
)
),
new Container(
height: 100.0,
child: new Center(
child: new Row(
children: <Widget>[
// Disabled, but tracking the slider above.
new Slider(value: _value / 100.0),
new Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Text((_value / 100.0).toStringAsFixed(2))
),
],
mainAxisAlignment: MainAxisAlignment.collapse
),
new Center(child: new Slider(value: _value / 100.0)),
new Center(
child: new Slider(
value: _discreteValue,
min: 0.0,
max: 100.0,
divisions: 5,
label: '${_discreteValue.round()}',
onChanged: (double value) {
setState(() {
_discreteValue = value;
});
}
)
)
)
])
),
]
)
);
}
}
......@@ -259,16 +259,9 @@ List<TextPainter> _initPainters(List<String> labels) {
List<TextPainter> painters = new List<TextPainter>(labels.length);
for (int i = 0; i < painters.length; ++i) {
String label = labels[i];
TextPainter painter = new TextPainter(
painters[i] = new TextPainter(
new TextSpan(style: style, text: label)
);
painter
..maxWidth = double.INFINITY
..maxHeight = double.INFINITY
..layout()
..maxWidth = painter.maxIntrinsicWidth
..layout();
painters[i] = painter;
)..layoutToMaxIntrinsicWidth();
}
return painters;
}
......
......@@ -243,7 +243,7 @@ class TextPainter {
}
ui.Paragraph _paragraph;
bool _needsLayout = true;
bool _needsLayout = false;
TextSpan _text;
/// The (potentially styled) text to paint.
......@@ -253,10 +253,15 @@ class TextPainter {
if (_text == value)
return;
_text = value;
ui.ParagraphBuilder builder = new ui.ParagraphBuilder();
_text.build(builder);
_paragraph = builder.build(_text.style?.paragraphStyle ?? new ui.ParagraphStyle());
_needsLayout = true;
if (_text != null) {
ui.ParagraphBuilder builder = new ui.ParagraphBuilder();
_text.build(builder);
_paragraph = builder.build(_text.style?.paragraphStyle ?? new ui.ParagraphStyle());
_needsLayout = true;
} else {
_paragraph = null;
_needsLayout = false;
}
}
/// The minimum width at which to layout the text.
......@@ -343,12 +348,33 @@ class TextPainter {
}
}
bool _lastLayoutWasToMaxIntrinsicWidth = false;
/// Computes the visual position of the glyphs for painting the text.
void layout() {
if (!_needsLayout)
return;
_paragraph.layout();
_needsLayout = false;
_lastLayoutWasToMaxIntrinsicWidth = false;
}
/// Computes the visual position of the glyphs using the unconstrainted max intrinsic width.
void layoutToMaxIntrinsicWidth() {
if (!_needsLayout && _lastLayoutWasToMaxIntrinsicWidth && width == maxIntrinsicWidth)
return;
_needsLayout = false;
_lastLayoutWasToMaxIntrinsicWidth = true;
_paragraph
..minWidth = 0.0
..maxWidth = double.INFINITY
..layout();
final double newMaxIntrinsicWidth = maxIntrinsicWidth;
_paragraph
..minWidth = newMaxIntrinsicWidth
..maxWidth = newMaxIntrinsicWidth
..layout();
assert(width == maxIntrinsicWidth);
}
/// Paints the text onto the given canvas at the given offset.
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test/test.dart';
void main() {
test('Slider can move when tapped', () {
testWidgets((WidgetTester tester) {
Key sliderKey = new UniqueKey();
double value = 0.0;
tester.pumpWidget(
new StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return new Material(
child: new Center(
child: new Slider(
key: sliderKey,
value: value,
onChanged: (double newValue) {
setState(() {
value = newValue;
});
}
)
)
);
}
)
);
expect(value, equals(0.0));
tester.tap(tester.findElementByKey(sliderKey));
expect(value, equals(0.5));
tester.pump(); // No animation should start.
expect(Scheduler.instance.transientCallbackCount, equals(0));
});
});
test('Slider take on discrete values', () {
testWidgets((WidgetTester tester) {
Key sliderKey = new UniqueKey();
double value = 0.0;
tester.pumpWidget(
new StatefulBuilder(
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;
});
}
)
)
);
}
)
);
expect(value, equals(0.0));
tester.tap(tester.findElementByKey(sliderKey));
expect(value, equals(50.0));
tester.scroll(tester.findElementByKey(sliderKey), const Offset(5.0, 0.0));
expect(value, equals(50.0));
tester.scroll(tester.findElementByKey(sliderKey), const Offset(40.0, 0.0));
expect(value, equals(80.0));
tester.pump(); // Starts animation.
expect(Scheduler.instance.transientCallbackCount, greaterThan(0));
tester.pump(const Duration(milliseconds: 200));
tester.pump(const Duration(milliseconds: 200));
tester.pump(const Duration(milliseconds: 200));
tester.pump(const Duration(milliseconds: 200));
// Animation complete.
expect(Scheduler.instance.transientCallbackCount, equals(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