-
Notifications
You must be signed in to change notification settings - Fork 28.7k
Slider Visual Update #14901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Slider Visual Update #14901
Conversation
3dd695a
to
e3958f2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Most of my feedback is just minor edits
@@ -2,46 +2,55 @@ | |||
// Use of this source code is governed by a BSD-style license that can be | |||
// found in the LICENSE file. | |||
|
|||
import 'dart:math' as math; | |||
import 'dart:math'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Within flutter it's conventional to use math.
for math package imports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, done.
/// 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. | ||
/// | ||
/// 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 | ||
/// given unbounded constraints, it will attempt to make the rail 144 pixels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful to insert a paragraph above this one that identifies the slider's parts (rail, thumb, value indicator, etc). It would be OK to just copy the material from SliderThemeData.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, done.
this.thumbOpenAtMin: false, | ||
}) : assert(value != null), | ||
assert(min != null), | ||
}) : assert(value != null), assert(min != null), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
linebreak between the asserts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoa, I think that was just me accidentally hitting the backspace key...
final String label; | ||
|
||
/// The color to use for the portion of the slider that has been selected. | ||
/// The color to use for the portion of the slider rail that has been | ||
/// selected. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case "selected" means between the slider's min limit and its value, right? It might be better to just say that than to ask the user to understand what the selected part of the rail is.
Note also: it looks like we're referring to this same part of the rail as "active".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I've been trying to unify the language to match the spec, but it's a little fragmented. Fixed.
@@ -132,30 +142,30 @@ class Slider extends StatefulWidget { | |||
/// A label to show above the slider when the slider is active. | |||
/// | |||
/// Typically used to display the value of a discrete slider. | |||
/// | |||
/// The label is rendered using the active [ThemeData]'s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you point out that the label is displayed within the value indicator, etc.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. It's not guaranteed to be within a custom indicator, but it is drawn by it.
); | ||
} | ||
|
||
class DefaultSliderThumbShape extends SliderComponentShape { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need Dartdoc here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
} | ||
|
||
class DefaultSliderValueIndicatorShape extends SliderComponentShape { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See earlier comment about dartdoc, not calling this DefaultFoo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to PaddleSliderValueIndicatorShape. Kind of awkward, I know, but descriptive.
@@ -306,6 +326,14 @@ class ThemeData { | |||
/// icons placed on top of the primary color (e.g. toolbar text). | |||
final Brightness primaryColorBrightness; | |||
|
|||
/// A lighter version of the primary color, used for showing subtle differences |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and below: I think it would be enough to just say "A lighter/darker version of the primary color".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
@@ -342,7 +370,7 @@ class ThemeData { | |||
/// | |||
/// * [InkSplash.splashFactory], which defines the default splash. | |||
/// * [InkRipple.splashFactory], which defines a splash that spreads out | |||
/// more aggresively than the default. | |||
/// more aggressively than the default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NICE
@@ -418,6 +446,10 @@ class ThemeData { | |||
/// An icon theme that contrasts with the accent color. | |||
final IconThemeData accentIconTheme; | |||
|
|||
/// A slider theme that describes the colors and shapes used in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
The colors and shapes used to render [Slider].
This is the default value of [SliderTheme.of].
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really a default value: it's the value, however it got set. But I like your idea, so I did something close.
b9fc3b2
to
eba21b5
Compare
LGTM |
This regressed our "undocumented members" benchmark by infinity percent. :-) (from 0 to 18.) |
description.add(new DiagnosticsProperty<IconThemeData>('iconTheme', iconTheme)); | ||
description.add(new DiagnosticsProperty<IconThemeData>('primaryIconTheme', primaryIconTheme)); | ||
description.add(new DiagnosticsProperty<IconThemeData>('accentIconTheme', accentIconTheme)); | ||
description.add(new DiagnosticsProperty<SliderThemeData>('sliderTheme', sliderTheme)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably mark most of these such that they don't show up in the debugDumpApp() output, because it basically makes the output unreadable as it stands today.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(also, minor nit: this would be more readable if all the lines were consistently wrapped or not wrapped)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, in another PR I've reformatted these with dartfmt, but with a line length of 200. That seems to keep the line breaks at bay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i was more worried about the problem of this all making debugDumpApp useless :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was wondering about that too. How does one mark them so they don't show up there? I didn't see a switch on the DiagnosticsProperty object. I added these because the previous toString was useless for debugging one of my tests.
@@ -732,7 +846,8 @@ class _IdentityThemeDataCacheKey { | |||
// We are explicitly ignoring the possibility that the types might not | |||
// match in the interests of speed. | |||
final _IdentityThemeDataCacheKey otherKey = other; | |||
return identical(baseTheme, otherKey.baseTheme) && identical(localTextGeometry, otherKey.localTextGeometry); | |||
return identical(baseTheme, otherKey.baseTheme) && | |||
identical(localTextGeometry, otherKey.localTextGeometry); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this lack of vertical alignment comes from dartfmt, perchance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is indenting a continued line by 4 spaces. Changing the line length to 200 seems to have solved that problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fwiw, i don't mind it being wrapped, i just want it to be more readable by keeping the vertical alignment consistent if it's wrapped
new DiagnosticsProperty<TextStyle>('caption', caption, defaultValue: defaultTheme.caption)); | ||
description.add( | ||
new DiagnosticsProperty<TextStyle>('button', button, defaultValue: defaultTheme.button)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this wrapping is also pretty hard to read
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also fixed in my other PR. #15078
// Test setting only the activeColor. | ||
await tester.pumpWidget(buildApp(activeColor: customColor1)); | ||
expect( | ||
sliderBox, paints..rect(color: customColor1)..rect(color: sliderTheme.inactiveRailColor)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's another example of dartfmt breaking you
super.debugFillProperties(description); | ||
if (color == null && _opacity == null && size == null) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should always add the properties, just give them default values in the description so they get hidden by default in the output
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I set them all to have default values of 'null'.
opacity: opacity ?? this.opacity, | ||
size: size ?? this.size | ||
); | ||
color: color ?? this.color, opacity: opacity ?? this.opacity, size: size ?? this.size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dartfmt broke you here too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. Fixed.
accentIconTheme, | ||
platform, | ||
) | ||
brightness, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like dartfmt broke you here too
opacity: other.opacity, | ||
size: other.size | ||
); | ||
return copyWith(color: other.color, opacity: other.opacity, size: other.size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
final TestGesture gesture = await tester.startGesture(center); | ||
await tester.pump(); | ||
await tester | ||
.pump(const Duration(milliseconds: 500)); // wait for value indicator animation to finish. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here
} | ||
|
||
Future<Null> expectValueIndicator( | ||
{bool isVisible, SliderThemeData theme, int divisions, bool enabled: true}) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here
child: new Theme( | ||
data: Theme.of(context).copyWith( | ||
sliderTheme: | ||
Theme.of(context).sliderTheme.copyWith(showValueIndicator: show)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks a bit weird
final Key sliderKey = new UniqueKey(); | ||
double value = 0.0; | ||
|
||
Widget buildSlider({ double textScaleFactor }) { | ||
Widget buildSlider( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here
gesture = await tester.startGesture(center); | ||
await tester.pump(); | ||
await tester | ||
.pump(const Duration(milliseconds: 500)); // wait for value indicator animation to finish. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
several times in this test the comment caused dartfmt to wrap a "pump" call inconsistently with the rest of the code
final ColorTween activeTickMarkEnableColor = new ColorTween( | ||
begin: _sliderTheme.disabledActiveTickMarkColor, end: _sliderTheme.activeTickMarkColor); | ||
final ColorTween inactiveTickMarkEnableColor = new ColorTween( | ||
begin: _sliderTheme.disabledInactiveTickMarkColor, end: _sliderTheme.inactiveTickMarkColor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these aren't really wrapped in the flutter style either, though here i don't think it impacts readabilty much
/// Defaults to the ambient [ThemeData.sliderTheme] if there is no | ||
/// [SliderTheme] in the given build context. | ||
/// | ||
/// Typical usage is as follows: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sample code should be in a ## Sample code
section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (in another PR)
/// defaults when assigning them to the slider theme component colors. | ||
/// | ||
/// This is used to generate the default slider theme for a [ThemeData]. | ||
factory SliderThemeData.materialDefaults({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would probably call this SliderThemeData.fromPrimaryColors
. The name SliderThemeData.materialDefaults
sounds like a constant that has material defaults, not a method that computes values from other values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Ok, but the reason I called it that was because the opacities that it overrides the given colors with, and the shapes it assigns for the components are all from the Material spec. This is also the method used to create the default theme for the Material ThemeData.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
being from the material spec is implied, this is the material library. :-)
valueIndicatorColor: Color.lerp(a.valueIndicatorColor, b.valueIndicatorColor, t), | ||
thumbShape: t < 0.5 ? a.thumbShape : b.thumbShape, | ||
valueIndicatorShape: t < 0.5 ? a.valueIndicatorShape : b.valueIndicatorShape, | ||
showValueIndicator: t < 0.5 ? a.showValueIndicator : b.showValueIndicator, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aw, the shapes can't lerp? :-( :-P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly no. They encompass a lot of behaviors that would have made lerping complex, so I decided to cut my losses and eliminate the lerpTo and lerpFrom. Not that we can't add them, or add a subclass of SliderComponentShape that has them.
|
||
@override | ||
bool operator ==(Object other) { | ||
if (other.runtimeType != runtimeType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you might want to do an identical()
test here first since there's so many subproperties and they're so often going to be identical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, done.
description.add(new DiagnosticsProperty<ShowValueIndicator>( | ||
'showValueIndicator', showValueIndicator, | ||
defaultValue: defaultData.showValueIndicator)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these are inconsistently wrapped which is weird
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've already fixed this in my other PR.
This implements an update to the look of the Slider widget. Specifically, it does the following: * Adds the ability to customize the colors of all components of the slider * Adds the ability to customize the shape of the slider thumb and value indicator * Adds the ability to show the value indicator on continuous sliders * Updates the default value indicator to be a "paddle" shape with new animations. * Changes the tick marks to be visible all the time on discrete sliders * Fixes a memory leak of an animation controller. * Removes "thumbOpenAtMin" flag, which is no longer needed, and can be emulated by the custom thumb shape support. It was not widely used. * Adds tests for all of the new features.
This implements an update to the look of the Slider widget.
Specifically, it does the following:
custom thumb shape support. It was not widely used.
Adds tests for all of the new features.