mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
This reverts commit e71cf1cdbe
.
This commit is contained in:
parent
ad07c4041a
commit
d14a301e41
@ -69,7 +69,6 @@ class _CustomRangeThumbShape extends RangeSliderThumbShape {
|
||||
@required SliderThemeData sliderTheme,
|
||||
TextDirection textDirection,
|
||||
Thumb thumb,
|
||||
bool isPressed,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final ColorTween colorTween = ColorTween(
|
||||
@ -131,8 +130,6 @@ class _CustomThumbShape extends SliderComponentShape {
|
||||
SliderThemeData sliderTheme,
|
||||
TextDirection textDirection,
|
||||
double value,
|
||||
double textScaleFactor,
|
||||
Size sizeWithOverflow,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final ColorTween colorTween = ColorTween(
|
||||
@ -172,8 +169,6 @@ class _CustomValueIndicatorShape extends SliderComponentShape {
|
||||
SliderThemeData sliderTheme,
|
||||
TextDirection textDirection,
|
||||
double value,
|
||||
double textScaleFactor,
|
||||
Size sizeWithOverflow,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final ColorTween enableColor = ColorTween(
|
||||
@ -273,21 +268,15 @@ class _SlidersState extends State<_Sliders> {
|
||||
),
|
||||
),
|
||||
),
|
||||
SliderTheme(
|
||||
data: const SliderThemeData(
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
),
|
||||
child: Slider.adaptive(
|
||||
label: _continuousValue.toStringAsFixed(6).toString(),
|
||||
value: _continuousValue,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
_continuousValue = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
Slider.adaptive(
|
||||
value: _continuousValue,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
_continuousValue = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('Continuous with Editable Numerical Value'),
|
||||
],
|
||||
@ -325,7 +314,7 @@ class _SlidersState extends State<_Sliders> {
|
||||
activeTrackColor: Colors.deepPurple,
|
||||
inactiveTrackColor: theme.colorScheme.onSurface.withOpacity(0.5),
|
||||
activeTickMarkColor: theme.colorScheme.onSurface.withOpacity(0.7),
|
||||
inactiveTickMarkColor: theme.colorScheme.surface.withOpacity(0.7),
|
||||
inactiveTickMarkColor: theme.colorScheme.surface.withOpacity(0.7),
|
||||
overlayColor: theme.colorScheme.onSurface.withOpacity(0.12),
|
||||
thumbColor: Colors.deepPurple,
|
||||
valueIndicatorColor: Colors.deepPurpleAccent,
|
||||
|
@ -21,12 +21,10 @@ ThemeData _buildDarkTheme() {
|
||||
final ColorScheme colorScheme = const ColorScheme.dark().copyWith(
|
||||
primary: primaryColor,
|
||||
secondary: secondaryColor,
|
||||
onPrimary: secondaryColor,
|
||||
);
|
||||
final ThemeData base = ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
accentColorBrightness: Brightness.dark,
|
||||
colorScheme: colorScheme,
|
||||
primaryColor: primaryColor,
|
||||
primaryColorDark: const Color(0xFF0050a0),
|
||||
primaryColorLight: secondaryColor,
|
||||
|
@ -22,11 +22,6 @@ import 'theme.dart';
|
||||
// RangeValues _dollarsRange = RangeValues(50, 100);
|
||||
// void setState(VoidCallback fn) { }
|
||||
|
||||
/// [RangeSlider] uses this callback to paint the value indicator on the overlay.
|
||||
/// Since the value indicator is painted on the Overlay; this method paints the
|
||||
/// value indicator in a [RenderBox] that appears in the [Overlay].
|
||||
typedef PaintRangeValueIndicator = void Function(PaintingContext context, Offset offset);
|
||||
|
||||
/// A Material Design range slider.
|
||||
///
|
||||
/// Used to select a range from a range of values.
|
||||
@ -132,7 +127,6 @@ class RangeSlider extends StatefulWidget {
|
||||
this.activeColor,
|
||||
this.inactiveColor,
|
||||
this.semanticFormatterCallback,
|
||||
this.useV2Slider = false,
|
||||
}) : assert(values != null),
|
||||
assert(min != null),
|
||||
assert(max != null),
|
||||
@ -141,7 +135,6 @@ class RangeSlider extends StatefulWidget {
|
||||
assert(values.start >= min && values.start <= max),
|
||||
assert(values.end >= min && values.end <= max),
|
||||
assert(divisions == null || divisions > 0),
|
||||
assert(useV2Slider != null),
|
||||
super(key: key);
|
||||
|
||||
/// The currently selected values for this range slider.
|
||||
@ -340,19 +333,6 @@ class RangeSlider extends StatefulWidget {
|
||||
/// {@end-tool}
|
||||
final RangeSemanticFormatterCallback semanticFormatterCallback;
|
||||
|
||||
/// Whether to use the updated Material spec version of the [RangeSlider].
|
||||
/// * The v2 [RangeSlider] has an updated value indicator that matches the latest specs.
|
||||
/// * The value indicator is painted on the Overlay.
|
||||
/// * The active track is bigger than the inactive track.
|
||||
/// * The thumb that is activated has elevation.
|
||||
/// * Updated value indicators in case they overlap with each other.
|
||||
/// * <https://groups.google.com/g/flutter-announce/c/69dmlKUL5Ew/m/tQh-ajiEAAAJl>
|
||||
///
|
||||
/// This is a temporary flag for migrating the slider from v1 to v2. Currently
|
||||
/// this defaults to false, because the changes may break existing tests. This
|
||||
/// value will be defaulted to true in the future.
|
||||
final bool useV2Slider;
|
||||
|
||||
// Touch width for the tap boundary of the slider thumbs.
|
||||
static const double _minTouchTargetWidth = kMinInteractiveDimension;
|
||||
|
||||
@ -374,7 +354,6 @@ class RangeSlider extends StatefulWidget {
|
||||
properties.add(StringProperty('labelEnd', labels?.end));
|
||||
properties.add(ColorProperty('activeColor', activeColor));
|
||||
properties.add(ColorProperty('inactiveColor', inactiveColor));
|
||||
properties.add(FlagProperty('useV2Slider', value: useV2Slider, ifFalse: 'useV1Slider'));
|
||||
properties.add(ObjectFlagProperty<ValueChanged<RangeValues>>.has('semanticFormatterCallback', semanticFormatterCallback));
|
||||
}
|
||||
}
|
||||
@ -398,10 +377,6 @@ class _RangeSliderState extends State<RangeSlider> with TickerProviderStateMixin
|
||||
AnimationController startPositionController;
|
||||
AnimationController endPositionController;
|
||||
Timer interactionTimer;
|
||||
// Value Indicator paint Animation that appears on the Overlay.
|
||||
PaintRangeValueIndicator paintTopValueIndicator;
|
||||
PaintRangeValueIndicator paintBottomValueIndicator;
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -545,7 +520,14 @@ class _RangeSliderState extends State<RangeSlider> with TickerProviderStateMixin
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
static const double _defaultTrackHeight = 2;
|
||||
static const RangeSliderTrackShape _defaultTrackShape = RoundedRectRangeSliderTrackShape();
|
||||
static const RangeSliderTickMarkShape _defaultTickMarkShape = RoundRangeSliderTickMarkShape();
|
||||
static const SliderComponentShape _defaultOverlayShape = RoundSliderOverlayShape();
|
||||
static const RangeSliderThumbShape _defaultThumbShape = RoundRangeSliderThumbShape();
|
||||
static const RangeSliderValueIndicatorShape _defaultValueIndicatorShape = PaddleRangeSliderValueIndicatorShape();
|
||||
static const ShowValueIndicator _defaultShowValueIndicator = ShowValueIndicator.onlyForDiscrete;
|
||||
static const double _defaultMinThumbSeparation = 8;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -561,29 +543,6 @@ class _RangeSliderState extends State<RangeSlider> with TickerProviderStateMixin
|
||||
// colors come from the ThemeData.colorScheme. These colors, along with
|
||||
// the default shapes and text styles are aligned to the Material
|
||||
// Guidelines.
|
||||
|
||||
final bool useV2Slider = widget.useV2Slider;
|
||||
final double _defaultTrackHeight = useV2Slider ? 4 : 2;
|
||||
final RangeSliderTrackShape _defaultTrackShape = RoundedRectRangeSliderTrackShape(useV2Slider: useV2Slider);
|
||||
final RangeSliderTickMarkShape _defaultTickMarkShape = RoundRangeSliderTickMarkShape(useV2Slider: useV2Slider);
|
||||
const SliderComponentShape _defaultOverlayShape = RoundSliderOverlayShape();
|
||||
final RangeSliderThumbShape _defaultThumbShape = RoundRangeSliderThumbShape(useV2Slider: useV2Slider);
|
||||
final RangeSliderValueIndicatorShape _defaultValueIndicatorShape = useV2Slider ? const RectangularRangeSliderValueIndicatorShape() : const PaddleRangeSliderValueIndicatorShape();
|
||||
const ShowValueIndicator _defaultShowValueIndicator = ShowValueIndicator.onlyForDiscrete;
|
||||
const double _defaultMinThumbSeparation = 8;
|
||||
|
||||
// The value indicator's color is not the same as the thumb and active track
|
||||
// (which can be defined by activeColor) if the
|
||||
// RectangularSliderValueIndicatorShape is used. In all other cases, the
|
||||
// value indicator is assumed to be the same as the active color.
|
||||
final RangeSliderValueIndicatorShape valueIndicatorShape = sliderTheme.rangeValueIndicatorShape ?? _defaultValueIndicatorShape;
|
||||
Color valueIndicatorColor;
|
||||
if (valueIndicatorShape is RectangularRangeSliderValueIndicatorShape) {
|
||||
valueIndicatorColor = sliderTheme.valueIndicatorColor ?? Color.alphaBlend(theme.colorScheme.onSurface.withOpacity(0.60), theme.colorScheme.surface.withOpacity(0.90));
|
||||
} else {
|
||||
valueIndicatorColor = widget.activeColor ?? sliderTheme.valueIndicatorColor ?? theme.colorScheme.primary;
|
||||
}
|
||||
|
||||
sliderTheme = sliderTheme.copyWith(
|
||||
trackHeight: sliderTheme.trackHeight ?? _defaultTrackHeight,
|
||||
activeTrackColor: widget.activeColor ?? sliderTheme.activeTrackColor ?? theme.colorScheme.primary,
|
||||
@ -596,14 +555,14 @@ class _RangeSliderState extends State<RangeSlider> with TickerProviderStateMixin
|
||||
disabledInactiveTickMarkColor: sliderTheme.disabledInactiveTickMarkColor ?? theme.colorScheme.onSurface.withOpacity(0.12),
|
||||
thumbColor: widget.activeColor ?? sliderTheme.thumbColor ?? theme.colorScheme.primary,
|
||||
overlappingShapeStrokeColor: sliderTheme.overlappingShapeStrokeColor ?? theme.colorScheme.surface,
|
||||
disabledThumbColor: sliderTheme.disabledThumbColor ?? Color.alphaBlend(theme.colorScheme.onSurface.withOpacity(.38), const Color(0xFFFFFFFF)),
|
||||
disabledThumbColor: sliderTheme.disabledThumbColor ?? theme.colorScheme.onSurface.withOpacity(0.38),
|
||||
overlayColor: widget.activeColor?.withOpacity(0.12) ?? sliderTheme.overlayColor ?? theme.colorScheme.primary.withOpacity(0.12),
|
||||
valueIndicatorColor: valueIndicatorColor,
|
||||
valueIndicatorColor: widget.activeColor ?? sliderTheme.valueIndicatorColor ?? theme.colorScheme.primary,
|
||||
rangeTrackShape: sliderTheme.rangeTrackShape ?? _defaultTrackShape,
|
||||
rangeTickMarkShape: sliderTheme.rangeTickMarkShape ?? _defaultTickMarkShape,
|
||||
rangeThumbShape: sliderTheme.rangeThumbShape ?? _defaultThumbShape,
|
||||
overlayShape: sliderTheme.overlayShape ?? _defaultOverlayShape,
|
||||
rangeValueIndicatorShape: valueIndicatorShape,
|
||||
rangeValueIndicatorShape: sliderTheme.rangeValueIndicatorShape ?? _defaultValueIndicatorShape,
|
||||
showValueIndicator: sliderTheme.showValueIndicator ?? _defaultShowValueIndicator,
|
||||
valueIndicatorTextStyle: sliderTheme.valueIndicatorTextStyle ?? theme.textTheme.bodyText1.copyWith(
|
||||
color: theme.colorScheme.onPrimary,
|
||||
@ -612,49 +571,19 @@ class _RangeSliderState extends State<RangeSlider> with TickerProviderStateMixin
|
||||
thumbSelector: sliderTheme.thumbSelector ?? _defaultRangeThumbSelector,
|
||||
);
|
||||
|
||||
// This size is used as the max bounds for the painting of the value
|
||||
// indicators. It must be kept in sync with the function with the same name
|
||||
// in slider.dart.
|
||||
Size _screenSize() => MediaQuery.of(context).size;
|
||||
|
||||
return CompositedTransformTarget(
|
||||
link: _layerLink,
|
||||
child: _RangeSliderRenderObjectWidget(
|
||||
values: _unlerpRangeValues(widget.values),
|
||||
divisions: widget.divisions,
|
||||
labels: widget.labels,
|
||||
sliderTheme: sliderTheme,
|
||||
textScaleFactor: MediaQuery.of(context).textScaleFactor,
|
||||
screenSize: _screenSize(),
|
||||
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
|
||||
onChangeStart: widget.onChangeStart != null ? _handleDragStart : null,
|
||||
onChangeEnd: widget.onChangeEnd != null ? _handleDragEnd : null,
|
||||
state: this,
|
||||
semanticFormatterCallback: widget.semanticFormatterCallback,
|
||||
useV2Slider: widget.useV2Slider,
|
||||
),
|
||||
return _RangeSliderRenderObjectWidget(
|
||||
values: _unlerpRangeValues(widget.values),
|
||||
divisions: widget.divisions,
|
||||
labels: widget.labels,
|
||||
sliderTheme: sliderTheme,
|
||||
textScaleFactor: MediaQuery.of(context).textScaleFactor,
|
||||
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
|
||||
onChangeStart: widget.onChangeStart != null ? _handleDragStart : null,
|
||||
onChangeEnd: widget.onChangeEnd != null ? _handleDragEnd : null,
|
||||
state: this,
|
||||
semanticFormatterCallback: widget.semanticFormatterCallback,
|
||||
);
|
||||
}
|
||||
|
||||
final LayerLink _layerLink = LayerLink();
|
||||
|
||||
OverlayEntry overlayEntry;
|
||||
|
||||
void showValueIndicator() {
|
||||
if (overlayEntry == null) {
|
||||
overlayEntry = OverlayEntry(
|
||||
builder: (BuildContext context) {
|
||||
return CompositedTransformFollower(
|
||||
link: _layerLink,
|
||||
child: _ValueIndicatorRenderObjectWidget(
|
||||
state: this,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
Overlay.of(context).insert(overlayEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
@ -665,13 +594,11 @@ class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
this.labels,
|
||||
this.sliderTheme,
|
||||
this.textScaleFactor,
|
||||
this.screenSize,
|
||||
this.onChanged,
|
||||
this.onChangeStart,
|
||||
this.onChangeEnd,
|
||||
this.state,
|
||||
this.semanticFormatterCallback,
|
||||
this.useV2Slider,
|
||||
}) : super(key: key);
|
||||
|
||||
final RangeValues values;
|
||||
@ -679,13 +606,11 @@ class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
final RangeLabels labels;
|
||||
final SliderThemeData sliderTheme;
|
||||
final double textScaleFactor;
|
||||
final Size screenSize;
|
||||
final ValueChanged<RangeValues> onChanged;
|
||||
final ValueChanged<RangeValues> onChangeStart;
|
||||
final ValueChanged<RangeValues> onChangeEnd;
|
||||
final RangeSemanticFormatterCallback semanticFormatterCallback;
|
||||
final _RangeSliderState state;
|
||||
final bool useV2Slider;
|
||||
|
||||
@override
|
||||
_RenderRangeSlider createRenderObject(BuildContext context) {
|
||||
@ -696,7 +621,6 @@ class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
sliderTheme: sliderTheme,
|
||||
theme: Theme.of(context),
|
||||
textScaleFactor: textScaleFactor,
|
||||
screenSize: screenSize,
|
||||
onChanged: onChanged,
|
||||
onChangeStart: onChangeStart,
|
||||
onChangeEnd: onChangeEnd,
|
||||
@ -704,7 +628,6 @@ class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
textDirection: Directionality.of(context),
|
||||
semanticFormatterCallback: semanticFormatterCallback,
|
||||
platform: Theme.of(context).platform,
|
||||
useV2Slider: useV2Slider,
|
||||
);
|
||||
}
|
||||
|
||||
@ -717,7 +640,6 @@ class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
..sliderTheme = sliderTheme
|
||||
..theme = Theme.of(context)
|
||||
..textScaleFactor = textScaleFactor
|
||||
..screenSize = screenSize
|
||||
..onChanged = onChanged
|
||||
..onChangeStart = onChangeStart
|
||||
..onChangeEnd = onChangeEnd
|
||||
@ -735,7 +657,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
SliderThemeData sliderTheme,
|
||||
ThemeData theme,
|
||||
double textScaleFactor,
|
||||
Size screenSize,
|
||||
TargetPlatform platform,
|
||||
ValueChanged<RangeValues> onChanged,
|
||||
RangeSemanticFormatterCallback semanticFormatterCallback,
|
||||
@ -743,7 +664,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
this.onChangeEnd,
|
||||
@required _RangeSliderState state,
|
||||
@required TextDirection textDirection,
|
||||
bool useV2Slider,
|
||||
}) : assert(values != null),
|
||||
assert(values.start >= 0.0 && values.start <= 1.0),
|
||||
assert(values.end >= 0.0 && values.end <= 1.0),
|
||||
@ -757,11 +677,9 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
_sliderTheme = sliderTheme,
|
||||
_theme = theme,
|
||||
_textScaleFactor = textScaleFactor,
|
||||
_screenSize = screenSize,
|
||||
_onChanged = onChanged,
|
||||
_state = state,
|
||||
_textDirection = textDirection,
|
||||
_useV2Slider = useV2Slider {
|
||||
_textDirection = textDirection {
|
||||
_updateLabelPainters();
|
||||
final GestureArenaTeam team = GestureArenaTeam();
|
||||
_drag = HorizontalDragGestureRecognizer()
|
||||
@ -782,12 +700,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
_valueIndicatorAnimation = CurvedAnimation(
|
||||
parent: _state.valueIndicatorController,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
)..addStatusListener((AnimationStatus status) {
|
||||
if (status == AnimationStatus.dismissed && _state.overlayEntry != null) {
|
||||
_state.overlayEntry.remove();
|
||||
_state.overlayEntry = null;
|
||||
}
|
||||
});
|
||||
);
|
||||
_enableAnimation = CurvedAnimation(
|
||||
parent: _state.enableController,
|
||||
curve: Curves.easeInOut,
|
||||
@ -936,15 +849,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
_updateLabelPainters();
|
||||
}
|
||||
|
||||
Size get screenSize => _screenSize;
|
||||
Size _screenSize;
|
||||
set screenSize(Size value) {
|
||||
if (value == screenSize)
|
||||
return;
|
||||
_screenSize = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
ValueChanged<RangeValues> get onChanged => _onChanged;
|
||||
ValueChanged<RangeValues> _onChanged;
|
||||
set onChanged(ValueChanged<RangeValues> value) {
|
||||
@ -1009,8 +913,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
return 0.05;
|
||||
}
|
||||
|
||||
final bool _useV2Slider;
|
||||
|
||||
void _updateLabelPainters() {
|
||||
_updateLabelPainter(Thumb.start);
|
||||
_updateLabelPainter(Thumb.end);
|
||||
@ -1107,7 +1009,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
}
|
||||
|
||||
void _startInteraction(Offset globalPosition) {
|
||||
_state.showValueIndicator();
|
||||
final double tapValue = _getValueFromGlobalPosition(globalPosition).clamp(0.0, 1.0) as double;
|
||||
_lastThumbSelection = sliderTheme.thumbSelector(textDirection, values, tapValue, _thumbSize, size, 0);
|
||||
|
||||
@ -1299,11 +1200,8 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
isEnabled: isEnabled,
|
||||
);
|
||||
|
||||
final bool startThumbSelected = _lastThumbSelection == Thumb.start;
|
||||
final bool endThumbSelected = _lastThumbSelection == Thumb.end;
|
||||
|
||||
if (!_overlayAnimation.isDismissed) {
|
||||
if (startThumbSelected) {
|
||||
if (_lastThumbSelection == Thumb.start) {
|
||||
_sliderTheme.overlayShape.paint(
|
||||
context,
|
||||
startThumbCenter,
|
||||
@ -1317,7 +1215,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
value: startValue,
|
||||
);
|
||||
}
|
||||
if (endThumbSelected) {
|
||||
if (_lastThumbSelection == Thumb.end) {
|
||||
_sliderTheme.overlayShape.paint(
|
||||
context,
|
||||
endThumbCenter,
|
||||
@ -1338,8 +1236,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
isEnabled: isEnabled,
|
||||
sliderTheme: _sliderTheme,
|
||||
).width;
|
||||
final double padding = _useV2Slider ? trackRect.height : tickMarkWidth;
|
||||
final double adjustedTrackWidth = trackRect.width - padding;
|
||||
final double adjustedTrackWidth = trackRect.width - tickMarkWidth;
|
||||
// If the tick marks would be too dense, don't bother painting them.
|
||||
if (adjustedTrackWidth / divisions >= 3.0 * tickMarkWidth) {
|
||||
final double dy = trackRect.center.dy;
|
||||
@ -1347,7 +1244,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
final double value = i / divisions;
|
||||
// The ticks are mapped to be within the track, so the tick mark width
|
||||
// must be subtracted from the track width.
|
||||
final double dx = trackRect.left + value * adjustedTrackWidth + padding / 2;
|
||||
final double dx = trackRect.left + value * adjustedTrackWidth + tickMarkWidth / 2;
|
||||
final Offset tickMarkOffset = Offset(dx, dy);
|
||||
_sliderTheme.rangeTickMarkShape.paint(
|
||||
context,
|
||||
@ -1376,27 +1273,22 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
final double bottomValue = isLastThumbStart ? endValue : startValue;
|
||||
final double topValue = isLastThumbStart ? startValue : endValue;
|
||||
final bool shouldPaintValueIndicators = isEnabled && labels != null && !_valueIndicatorAnimation.isDismissed && showValueIndicator;
|
||||
final Size resolvedscreenSize = screenSize.isEmpty ? size : screenSize;
|
||||
|
||||
if (shouldPaintValueIndicators) {
|
||||
_state.paintBottomValueIndicator = (PaintingContext context, Offset offset) {
|
||||
_sliderTheme.rangeValueIndicatorShape.paint(
|
||||
context,
|
||||
bottomThumbCenter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
isOnTop: false,
|
||||
labelPainter: bottomLabelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
thumb: bottomThumb,
|
||||
value: bottomValue,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: resolvedscreenSize,
|
||||
);
|
||||
};
|
||||
_sliderTheme.rangeValueIndicatorShape.paint(
|
||||
context,
|
||||
bottomThumbCenter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
isOnTop: false,
|
||||
labelPainter: bottomLabelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
thumb: bottomThumb,
|
||||
value: bottomValue,
|
||||
);
|
||||
}
|
||||
|
||||
_sliderTheme.rangeThumbShape.paint(
|
||||
@ -1409,7 +1301,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
textDirection: textDirection,
|
||||
sliderTheme: _sliderTheme,
|
||||
thumb: bottomThumb,
|
||||
isPressed: bottomThumb == Thumb.start ? startThumbSelected : endThumbSelected,
|
||||
);
|
||||
|
||||
if (shouldPaintValueIndicators) {
|
||||
@ -1418,29 +1309,15 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
center: startThumbCenter,
|
||||
labelPainter: _startLabelPainter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: resolvedscreenSize,
|
||||
);
|
||||
final double endOffset = sliderTheme.rangeValueIndicatorShape.getHorizontalShift(
|
||||
parentBox: this,
|
||||
center: endThumbCenter,
|
||||
labelPainter: _endLabelPainter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: resolvedscreenSize,
|
||||
);
|
||||
final double startHalfWidth = sliderTheme.rangeValueIndicatorShape.getPreferredSize(
|
||||
isEnabled,
|
||||
isDiscrete,
|
||||
labelPainter: _startLabelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
).width / 2;
|
||||
final double endHalfWidth = sliderTheme.rangeValueIndicatorShape.getPreferredSize(
|
||||
isEnabled,
|
||||
isDiscrete,
|
||||
labelPainter: _endLabelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
).width / 2;
|
||||
final double startHalfWidth = sliderTheme.rangeValueIndicatorShape.getPreferredSize(isEnabled, isDiscrete, labelPainter: _startLabelPainter).width / 2;
|
||||
final double endHalfWidth = sliderTheme.rangeValueIndicatorShape.getPreferredSize(isEnabled, isDiscrete, labelPainter: _endLabelPainter).width / 2;
|
||||
double innerOverflow = startHalfWidth + endHalfWidth;
|
||||
switch (textDirection) {
|
||||
case TextDirection.ltr:
|
||||
@ -1453,37 +1330,32 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
break;
|
||||
}
|
||||
|
||||
_state.paintTopValueIndicator = (PaintingContext context, Offset offset) {
|
||||
_sliderTheme.rangeValueIndicatorShape.paint(
|
||||
context,
|
||||
topThumbCenter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
isOnTop: thumbDelta < innerOverflow,
|
||||
labelPainter: topLabelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
thumb: topThumb,
|
||||
value: topValue,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: resolvedscreenSize,
|
||||
);
|
||||
};
|
||||
_sliderTheme.rangeValueIndicatorShape.paint(
|
||||
context,
|
||||
topThumbCenter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
isOnTop: thumbDelta < innerOverflow,
|
||||
labelPainter: topLabelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
thumb: topThumb,
|
||||
value: topValue,
|
||||
);
|
||||
}
|
||||
|
||||
_sliderTheme.rangeThumbShape.paint(
|
||||
context,
|
||||
topThumbCenter,
|
||||
activationAnimation: _overlayAnimation,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
isOnTop: thumbDelta < sliderTheme.rangeThumbShape.getPreferredSize(isEnabled, isDiscrete).width,
|
||||
textDirection: textDirection,
|
||||
sliderTheme: _sliderTheme,
|
||||
thumb: topThumb,
|
||||
isPressed: topThumb == Thumb.start ? startThumbSelected : endThumbSelected,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1547,66 +1419,3 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
|
||||
return (value - _semanticActionUnit).clamp(0.0, 1.0) as double;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _ValueIndicatorRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
const _ValueIndicatorRenderObjectWidget({
|
||||
this.state,
|
||||
});
|
||||
|
||||
final _RangeSliderState state;
|
||||
|
||||
@override
|
||||
_RenderValueIndicator createRenderObject(BuildContext context) {
|
||||
return _RenderValueIndicator(
|
||||
state: state,
|
||||
);
|
||||
}
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, _RenderValueIndicator renderObject) {
|
||||
renderObject._state = state;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderValueIndicator extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
_RenderValueIndicator({
|
||||
_RangeSliderState state,
|
||||
}) :_state = state {
|
||||
_valueIndicatorAnimation = CurvedAnimation(
|
||||
parent: _state.valueIndicatorController,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
}
|
||||
|
||||
Animation<double> _valueIndicatorAnimation;
|
||||
_RangeSliderState _state;
|
||||
|
||||
@override
|
||||
bool get sizedByParent => true;
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
_valueIndicatorAnimation.addListener(markNeedsPaint);
|
||||
_state.startPositionController.addListener(markNeedsPaint);
|
||||
_state.endPositionController.addListener(markNeedsPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
void detach() {
|
||||
_valueIndicatorAnimation.removeListener(markNeedsPaint);
|
||||
_state.startPositionController.removeListener(markNeedsPaint);
|
||||
_state.endPositionController.removeListener(markNeedsPaint);
|
||||
super.detach();
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (_state.paintBottomValueIndicator != null) {
|
||||
_state.paintBottomValueIndicator(context, offset);
|
||||
}
|
||||
if (_state.paintTopValueIndicator != null) {
|
||||
_state.paintTopValueIndicator(context, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,11 +31,6 @@ import 'theme.dart';
|
||||
/// * [Slider.semanticFormatterCallback], which shows an example use case.
|
||||
typedef SemanticFormatterCallback = String Function(double value);
|
||||
|
||||
/// [Slider] uses this callback to paint the value indicator on the overlay.
|
||||
/// Since the value indicator is painted on the Overlay; this method paints the
|
||||
/// value indicator in a [RenderBox] that appears in the [Overlay].
|
||||
typedef PaintValueIndicator = void Function(PaintingContext context, Offset offset);
|
||||
|
||||
enum _SliderType { material, adaptive }
|
||||
|
||||
/// A Material Design slider.
|
||||
@ -129,7 +124,6 @@ class Slider extends StatefulWidget {
|
||||
this.activeColor,
|
||||
this.inactiveColor,
|
||||
this.semanticFormatterCallback,
|
||||
this.useV2Slider = false,
|
||||
}) : _sliderType = _SliderType.material,
|
||||
assert(value != null),
|
||||
assert(min != null),
|
||||
@ -137,7 +131,6 @@ class Slider extends StatefulWidget {
|
||||
assert(min <= max),
|
||||
assert(value >= min && value <= max),
|
||||
assert(divisions == null || divisions > 0),
|
||||
assert(useV2Slider != null),
|
||||
super(key: key);
|
||||
|
||||
/// Creates a [CupertinoSlider] if the target platform is iOS, creates a
|
||||
@ -160,7 +153,6 @@ class Slider extends StatefulWidget {
|
||||
this.activeColor,
|
||||
this.inactiveColor,
|
||||
this.semanticFormatterCallback,
|
||||
this.useV2Slider = false,
|
||||
}) : _sliderType = _SliderType.adaptive,
|
||||
assert(value != null),
|
||||
assert(min != null),
|
||||
@ -168,7 +160,6 @@ class Slider extends StatefulWidget {
|
||||
assert(min <= max),
|
||||
assert(value >= min && value <= max),
|
||||
assert(divisions == null || divisions > 0),
|
||||
assert(useV2Slider != null),
|
||||
super(key: key);
|
||||
|
||||
/// The currently selected value for this slider.
|
||||
@ -383,19 +374,6 @@ class Slider extends StatefulWidget {
|
||||
/// Ignored if this slider is created with [Slider.adaptive]
|
||||
final SemanticFormatterCallback semanticFormatterCallback;
|
||||
|
||||
/// Whether to use the updated Material spec version of the [Slider].
|
||||
/// * The v2 Slider has an updated value indicator that matches the latest specs.
|
||||
/// * The value indicator is painted on the Overlay.
|
||||
/// * The active track is bigger than the inactive track.
|
||||
/// * The thumb that is activated has elevation.
|
||||
/// * Updated value indicators in case they overlap with each other.
|
||||
/// * <https://groups.google.com/g/flutter-announce/c/69dmlKUL5Ew/m/tQh-ajiEAAAJl>
|
||||
///
|
||||
/// This is a temporary flag for migrating the slider from v1 to v2. To avoid
|
||||
/// unexpected breaking changes, this value should be set to true. Setting
|
||||
/// this to false is considered deprecated.
|
||||
final bool useV2Slider;
|
||||
|
||||
final _SliderType _sliderType ;
|
||||
|
||||
@override
|
||||
@ -414,7 +392,6 @@ class Slider extends StatefulWidget {
|
||||
properties.add(StringProperty('label', label));
|
||||
properties.add(ColorProperty('activeColor', activeColor));
|
||||
properties.add(ColorProperty('inactiveColor', inactiveColor));
|
||||
properties.add(FlagProperty('useV2Slider', value: useV2Slider, ifFalse: 'useV1Slider'));
|
||||
properties.add(ObjectFlagProperty<ValueChanged<double>>.has('semanticFormatterCallback', semanticFormatterCallback));
|
||||
}
|
||||
}
|
||||
@ -435,8 +412,6 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
||||
// and the next on a discrete slider.
|
||||
AnimationController positionController;
|
||||
Timer interactionTimer;
|
||||
// Value Indicator Animation that appears on the Overlay.
|
||||
PaintValueIndicator paintValueIndicator;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -504,6 +479,14 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
||||
return widget.max > widget.min ? (value - widget.min) / (widget.max - widget.min) : 0.0;
|
||||
}
|
||||
|
||||
static const double _defaultTrackHeight = 2;
|
||||
static const SliderTrackShape _defaultTrackShape = RoundedRectSliderTrackShape();
|
||||
static const SliderTickMarkShape _defaultTickMarkShape = RoundSliderTickMarkShape();
|
||||
static const SliderComponentShape _defaultOverlayShape = RoundSliderOverlayShape();
|
||||
static const SliderComponentShape _defaultThumbShape = RoundSliderThumbShape();
|
||||
static const SliderComponentShape _defaultValueIndicatorShape = PaddleSliderValueIndicatorShape();
|
||||
static const ShowValueIndicator _defaultShowValueIndicator = ShowValueIndicator.onlyForDiscrete;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMaterial(context));
|
||||
@ -542,28 +525,6 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
||||
// colors come from the ThemeData.colorScheme. These colors, along with
|
||||
// the default shapes and text styles are aligned to the Material
|
||||
// Guidelines.
|
||||
|
||||
final bool useV2Slider = widget.useV2Slider;
|
||||
final double _defaultTrackHeight = useV2Slider ? 4 : 2;
|
||||
final SliderTrackShape _defaultTrackShape = RoundedRectSliderTrackShape(useV2Slider: useV2Slider);
|
||||
final SliderTickMarkShape _defaultTickMarkShape = RoundSliderTickMarkShape(useV2Slider: useV2Slider);
|
||||
const SliderComponentShape _defaultOverlayShape = RoundSliderOverlayShape();
|
||||
final SliderComponentShape _defaultThumbShape = RoundSliderThumbShape(useV2Slider: useV2Slider);
|
||||
final SliderComponentShape _defaultValueIndicatorShape = useV2Slider ? const RectangularSliderValueIndicatorShape() : const PaddleSliderValueIndicatorShape();
|
||||
const ShowValueIndicator _defaultShowValueIndicator = ShowValueIndicator.onlyForDiscrete;
|
||||
|
||||
// The value indicator's color is not the same as the thumb and active track
|
||||
// (which can be defined by activeColor) if the
|
||||
// RectangularSliderValueIndicatorShape is used. In all other cases, the
|
||||
// value indicator is assumed to be the same as the active color.
|
||||
final SliderComponentShape valueIndicatorShape = sliderTheme.valueIndicatorShape ?? _defaultValueIndicatorShape;
|
||||
Color valueIndicatorColor;
|
||||
if (valueIndicatorShape is RectangularSliderValueIndicatorShape) {
|
||||
valueIndicatorColor = sliderTheme.valueIndicatorColor ?? Color.alphaBlend(theme.colorScheme.onSurface.withOpacity(0.60), theme.colorScheme.surface.withOpacity(0.90));
|
||||
} else {
|
||||
valueIndicatorColor = widget.activeColor ?? sliderTheme.valueIndicatorColor ?? theme.colorScheme.primary;
|
||||
}
|
||||
|
||||
sliderTheme = sliderTheme.copyWith(
|
||||
trackHeight: sliderTheme.trackHeight ?? _defaultTrackHeight,
|
||||
activeTrackColor: widget.activeColor ?? sliderTheme.activeTrackColor ?? theme.colorScheme.primary,
|
||||
@ -575,41 +536,31 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
||||
disabledActiveTickMarkColor: sliderTheme.disabledActiveTickMarkColor ?? theme.colorScheme.onPrimary.withOpacity(0.12),
|
||||
disabledInactiveTickMarkColor: sliderTheme.disabledInactiveTickMarkColor ?? theme.colorScheme.onSurface.withOpacity(0.12),
|
||||
thumbColor: widget.activeColor ?? sliderTheme.thumbColor ?? theme.colorScheme.primary,
|
||||
disabledThumbColor: sliderTheme.disabledThumbColor ?? Color.alphaBlend(theme.colorScheme.onSurface.withOpacity(.38), const Color(0xFFFFFFFF)),
|
||||
disabledThumbColor: sliderTheme.disabledThumbColor ?? theme.colorScheme.onSurface.withOpacity(0.38),
|
||||
overlayColor: widget.activeColor?.withOpacity(0.12) ?? sliderTheme.overlayColor ?? theme.colorScheme.primary.withOpacity(0.12),
|
||||
valueIndicatorColor: valueIndicatorColor,
|
||||
valueIndicatorColor: widget.activeColor ?? sliderTheme.valueIndicatorColor ?? theme.colorScheme.primary,
|
||||
trackShape: sliderTheme.trackShape ?? _defaultTrackShape,
|
||||
tickMarkShape: sliderTheme.tickMarkShape ?? _defaultTickMarkShape,
|
||||
thumbShape: sliderTheme.thumbShape ?? _defaultThumbShape,
|
||||
overlayShape: sliderTheme.overlayShape ?? _defaultOverlayShape,
|
||||
valueIndicatorShape: valueIndicatorShape,
|
||||
valueIndicatorShape: sliderTheme.valueIndicatorShape ?? _defaultValueIndicatorShape,
|
||||
showValueIndicator: sliderTheme.showValueIndicator ?? _defaultShowValueIndicator,
|
||||
valueIndicatorTextStyle: sliderTheme.valueIndicatorTextStyle ?? theme.textTheme.bodyText1.copyWith(
|
||||
color: theme.colorScheme.onPrimary,
|
||||
),
|
||||
);
|
||||
|
||||
// This size is used as the max bounds for the painting of the value
|
||||
// indicators It must be kept in sync with the function with the same name
|
||||
// in range_slider.dart.
|
||||
Size _screenSize() => MediaQuery.of(context).size;
|
||||
|
||||
return CompositedTransformTarget(
|
||||
link: _layerLink,
|
||||
child: _SliderRenderObjectWidget(
|
||||
value: _unlerp(widget.value),
|
||||
divisions: widget.divisions,
|
||||
label: widget.label,
|
||||
sliderTheme: sliderTheme,
|
||||
textScaleFactor: MediaQuery.of(context).textScaleFactor,
|
||||
screenSize: _screenSize(),
|
||||
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
|
||||
onChangeStart: widget.onChangeStart != null ? _handleDragStart : null,
|
||||
onChangeEnd: widget.onChangeEnd != null ? _handleDragEnd : null,
|
||||
state: this,
|
||||
semanticFormatterCallback: widget.semanticFormatterCallback,
|
||||
useV2Slider: widget.useV2Slider,
|
||||
),
|
||||
return _SliderRenderObjectWidget(
|
||||
value: _unlerp(widget.value),
|
||||
divisions: widget.divisions,
|
||||
label: widget.label,
|
||||
sliderTheme: sliderTheme,
|
||||
mediaQueryData: MediaQuery.of(context),
|
||||
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
|
||||
onChangeStart: widget.onChangeStart != null ? _handleDragStart : null,
|
||||
onChangeEnd: widget.onChangeEnd != null ? _handleDragEnd : null,
|
||||
state: this,
|
||||
semanticFormatterCallback: widget.semanticFormatterCallback,
|
||||
);
|
||||
}
|
||||
|
||||
@ -631,28 +582,8 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
|
||||
),
|
||||
);
|
||||
}
|
||||
final LayerLink _layerLink = LayerLink();
|
||||
|
||||
OverlayEntry overlayEntry;
|
||||
|
||||
void showValueIndicator() {
|
||||
if (overlayEntry == null) {
|
||||
overlayEntry = OverlayEntry(
|
||||
builder: (BuildContext context) {
|
||||
return CompositedTransformFollower(
|
||||
link: _layerLink,
|
||||
child: _ValueIndicatorRenderObjectWidget(
|
||||
state: this,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
Overlay.of(context).insert(overlayEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
const _SliderRenderObjectWidget({
|
||||
Key key,
|
||||
@ -660,28 +591,24 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
this.divisions,
|
||||
this.label,
|
||||
this.sliderTheme,
|
||||
this.textScaleFactor,
|
||||
this.screenSize,
|
||||
this.mediaQueryData,
|
||||
this.onChanged,
|
||||
this.onChangeStart,
|
||||
this.onChangeEnd,
|
||||
this.state,
|
||||
this.semanticFormatterCallback,
|
||||
this.useV2Slider,
|
||||
}) : super(key: key);
|
||||
|
||||
final double value;
|
||||
final int divisions;
|
||||
final String label;
|
||||
final SliderThemeData sliderTheme;
|
||||
final double textScaleFactor;
|
||||
final Size screenSize;
|
||||
final MediaQueryData mediaQueryData;
|
||||
final ValueChanged<double> onChanged;
|
||||
final ValueChanged<double> onChangeStart;
|
||||
final ValueChanged<double> onChangeEnd;
|
||||
final SemanticFormatterCallback semanticFormatterCallback;
|
||||
final _SliderState state;
|
||||
final bool useV2Slider;
|
||||
|
||||
@override
|
||||
_RenderSlider createRenderObject(BuildContext context) {
|
||||
@ -690,8 +617,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
divisions: divisions,
|
||||
label: label,
|
||||
sliderTheme: sliderTheme,
|
||||
textScaleFactor: textScaleFactor,
|
||||
screenSize: screenSize,
|
||||
mediaQueryData: mediaQueryData,
|
||||
onChanged: onChanged,
|
||||
onChangeStart: onChangeStart,
|
||||
onChangeEnd: onChangeEnd,
|
||||
@ -699,7 +625,6 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
textDirection: Directionality.of(context),
|
||||
semanticFormatterCallback: semanticFormatterCallback,
|
||||
platform: Theme.of(context).platform,
|
||||
useV2Slider: useV2Slider,
|
||||
);
|
||||
}
|
||||
|
||||
@ -711,8 +636,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
..label = label
|
||||
..sliderTheme = sliderTheme
|
||||
..theme = Theme.of(context)
|
||||
..textScaleFactor = textScaleFactor
|
||||
..screenSize = screenSize
|
||||
..mediaQueryData = mediaQueryData
|
||||
..onChanged = onChanged
|
||||
..onChangeStart = onChangeStart
|
||||
..onChangeEnd = onChangeEnd
|
||||
@ -730,8 +654,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
int divisions,
|
||||
String label,
|
||||
SliderThemeData sliderTheme,
|
||||
double textScaleFactor,
|
||||
Size screenSize,
|
||||
MediaQueryData mediaQueryData,
|
||||
TargetPlatform platform,
|
||||
ValueChanged<double> onChanged,
|
||||
SemanticFormatterCallback semanticFormatterCallback,
|
||||
@ -739,22 +662,19 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
this.onChangeEnd,
|
||||
@required _SliderState state,
|
||||
@required TextDirection textDirection,
|
||||
bool useV2Slider,
|
||||
}) : assert(value != null && value >= 0.0 && value <= 1.0),
|
||||
assert(state != null),
|
||||
assert(textDirection != null),
|
||||
_platform = platform,
|
||||
_semanticFormatterCallback = semanticFormatterCallback,
|
||||
_label = label,
|
||||
_value = value,
|
||||
_divisions = divisions,
|
||||
_sliderTheme = sliderTheme,
|
||||
_textScaleFactor = textScaleFactor,
|
||||
_screenSize = screenSize,
|
||||
_onChanged = onChanged,
|
||||
_state = state,
|
||||
_textDirection = textDirection,
|
||||
_useV2Slider = useV2Slider {
|
||||
assert(state != null),
|
||||
assert(textDirection != null),
|
||||
_platform = platform,
|
||||
_semanticFormatterCallback = semanticFormatterCallback,
|
||||
_label = label,
|
||||
_value = value,
|
||||
_divisions = divisions,
|
||||
_sliderTheme = sliderTheme,
|
||||
_mediaQueryData = mediaQueryData,
|
||||
_onChanged = onChanged,
|
||||
_state = state,
|
||||
_textDirection = textDirection {
|
||||
_updateLabelPainter();
|
||||
final GestureArenaTeam team = GestureArenaTeam();
|
||||
_drag = HorizontalDragGestureRecognizer()
|
||||
@ -775,12 +695,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
_valueIndicatorAnimation = CurvedAnimation(
|
||||
parent: _state.valueIndicatorController,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
)..addStatusListener((AnimationStatus status) {
|
||||
if (status == AnimationStatus.dismissed && _state.overlayEntry != null) {
|
||||
_state.overlayEntry.remove();
|
||||
_state.overlayEntry = null;
|
||||
}
|
||||
});
|
||||
);
|
||||
_enableAnimation = CurvedAnimation(
|
||||
parent: _state.enableController,
|
||||
curve: Curves.easeInOut,
|
||||
@ -911,26 +826,18 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
double get textScaleFactor => _textScaleFactor;
|
||||
double _textScaleFactor;
|
||||
set textScaleFactor(double value) {
|
||||
if (value == _textScaleFactor) {
|
||||
MediaQueryData get mediaQueryData => _mediaQueryData;
|
||||
MediaQueryData _mediaQueryData;
|
||||
set mediaQueryData(MediaQueryData value) {
|
||||
if (value == _mediaQueryData) {
|
||||
return;
|
||||
}
|
||||
_textScaleFactor = value;
|
||||
_mediaQueryData = value;
|
||||
// Media query data includes the textScaleFactor, so we need to update the
|
||||
// label painter.
|
||||
_updateLabelPainter();
|
||||
}
|
||||
|
||||
Size get screenSize => _screenSize;
|
||||
Size _screenSize;
|
||||
set screenSize(Size value) {
|
||||
if (value == _screenSize) {
|
||||
return;
|
||||
}
|
||||
_screenSize = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
ValueChanged<double> get onChanged => _onChanged;
|
||||
ValueChanged<double> _onChanged;
|
||||
set onChanged(ValueChanged<double> value) {
|
||||
@ -964,8 +871,6 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
_updateLabelPainter();
|
||||
}
|
||||
|
||||
final bool _useV2Slider;
|
||||
|
||||
bool get showValueIndicator {
|
||||
bool showValueIndicator;
|
||||
switch (_sliderTheme.showValueIndicator) {
|
||||
@ -1010,7 +915,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
text: label,
|
||||
)
|
||||
..textDirection = textDirection
|
||||
..textScaleFactor = textScaleFactor
|
||||
..textScaleFactor = _mediaQueryData.textScaleFactor
|
||||
..layout();
|
||||
} else {
|
||||
_labelPainter.text = null;
|
||||
@ -1070,7 +975,6 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
}
|
||||
|
||||
void _startInteraction(Offset globalPosition) {
|
||||
_state.showValueIndicator();
|
||||
if (isInteractive) {
|
||||
_active = true;
|
||||
// We supply the *current* value as the start location, so that if we have
|
||||
@ -1104,7 +1008,6 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
_active = false;
|
||||
_currentDragValue = 0.0;
|
||||
_state.overlayController.reverse();
|
||||
|
||||
if (showValueIndicator && _state.interactionTimer == null) {
|
||||
_state.valueIndicatorController.reverse();
|
||||
}
|
||||
@ -1227,8 +1130,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
isEnabled: isInteractive,
|
||||
sliderTheme: _sliderTheme,
|
||||
).width;
|
||||
final double padding = _useV2Slider ? trackRect.height : tickMarkWidth;
|
||||
final double adjustedTrackWidth = trackRect.width - padding;
|
||||
final double adjustedTrackWidth = trackRect.width - tickMarkWidth;
|
||||
// If the tick marks would be too dense, don't bother painting them.
|
||||
if (adjustedTrackWidth / divisions >= 3.0 * tickMarkWidth) {
|
||||
final double dy = trackRect.center.dy;
|
||||
@ -1236,7 +1138,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
final double value = i / divisions;
|
||||
// The ticks are mapped to be within the track, so the tick mark width
|
||||
// must be subtracted from the track width.
|
||||
final double dx = trackRect.left + value * adjustedTrackWidth + padding / 2;
|
||||
final double dx = trackRect.left + value * adjustedTrackWidth + tickMarkWidth / 2;
|
||||
final Offset tickMarkOffset = Offset(dx, dy);
|
||||
_sliderTheme.tickMarkShape.paint(
|
||||
context,
|
||||
@ -1254,36 +1156,31 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
|
||||
if (isInteractive && label != null && !_valueIndicatorAnimation.isDismissed) {
|
||||
if (showValueIndicator) {
|
||||
_state.paintValueIndicator = (PaintingContext context, Offset offset) {
|
||||
_sliderTheme.valueIndicatorShape.paint(
|
||||
context,
|
||||
offset + thumbCenter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
labelPainter: _labelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
value: _value,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: screenSize.isEmpty ? size : screenSize,
|
||||
);
|
||||
};
|
||||
_sliderTheme.valueIndicatorShape.paint(
|
||||
context,
|
||||
thumbCenter,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
labelPainter: _labelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
value: _value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_sliderTheme.thumbShape.paint(
|
||||
context,
|
||||
thumbCenter,
|
||||
activationAnimation: _overlayAnimation,
|
||||
activationAnimation: _valueIndicatorAnimation,
|
||||
enableAnimation: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
labelPainter: _labelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
sizeWithOverflow: screenSize.isEmpty ? size : screenSize,
|
||||
value: _value,
|
||||
);
|
||||
}
|
||||
@ -1323,59 +1220,3 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ValueIndicatorRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
const _ValueIndicatorRenderObjectWidget({
|
||||
this.state,
|
||||
});
|
||||
|
||||
final _SliderState state;
|
||||
|
||||
@override
|
||||
_RenderValueIndicator createRenderObject(BuildContext context) {
|
||||
return _RenderValueIndicator(
|
||||
state: state,
|
||||
);
|
||||
}
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, _RenderValueIndicator renderObject) {
|
||||
renderObject._state = state;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderValueIndicator extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
_RenderValueIndicator({
|
||||
_SliderState state,
|
||||
}) : _state = state {
|
||||
_valueIndicatorAnimation = CurvedAnimation(
|
||||
parent: _state.valueIndicatorController,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
}
|
||||
Animation<double> _valueIndicatorAnimation;
|
||||
_SliderState _state;
|
||||
|
||||
@override
|
||||
bool get sizedByParent => true;
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
_valueIndicatorAnimation.addListener(markNeedsPaint);
|
||||
_state.positionController.addListener(markNeedsPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
void detach() {
|
||||
_valueIndicatorAnimation.removeListener(markNeedsPaint);
|
||||
_state.positionController.removeListener(markNeedsPaint);
|
||||
super.detach();
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (_state.paintValueIndicator != null) {
|
||||
_state.paintValueIndicator(context, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -94,24 +94,6 @@ void main() {
|
||||
]);
|
||||
});
|
||||
|
||||
testWidgets('Slider V2 uses ThemeData slider theme if present', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
primarySwatch: Colors.red,
|
||||
);
|
||||
final SliderThemeData sliderTheme = theme.sliderTheme;
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, enabled: false, useV2Slider: true));
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..rrect(color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(color: sliderTheme.disabledInactiveTrackColor),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider uses ThemeData slider theme if present', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
@ -130,28 +112,6 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider V2 overrides ThemeData theme if SliderTheme present', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
primarySwatch: Colors.red,
|
||||
);
|
||||
final SliderThemeData sliderTheme = theme.sliderTheme;
|
||||
final SliderThemeData customTheme = sliderTheme.copyWith(
|
||||
activeTrackColor: Colors.purple,
|
||||
inactiveTrackColor: Colors.purple.withAlpha(0x3d),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, enabled: false, useV2Slider: true));
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..rrect(color: customTheme.disabledActiveTrackColor)
|
||||
..rrect(color: customTheme.disabledInactiveTrackColor),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider overrides ThemeData theme if SliderTheme present', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
@ -258,40 +218,6 @@ void main() {
|
||||
expect(lerp.valueIndicatorTextStyle.color, equals(middleGrey.withAlpha(0xff)));
|
||||
});
|
||||
|
||||
testWidgets('Slider V2 track draws correctly', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
primarySwatch: Colors.blue,
|
||||
);
|
||||
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, useV2Slider: true));
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
const Radius radius = Radius.circular(2);
|
||||
const Radius activatedRadius = Radius.circular(3);
|
||||
|
||||
// The enabled slider thumb has track segments that extend to and from
|
||||
// the center of the thumb.
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 297.0, 212.0, 303.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.activeTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 776.0, 302.0, topRight: radius, bottomRight: radius), color: sliderTheme.inactiveTrackColor),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false, useV2Slider: true));
|
||||
await tester.pumpAndSettle(); // wait for disable animation
|
||||
|
||||
// The disabled slider thumb is the same size as the enabled thumb.
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 297.0, 212.0, 303.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 298.0, 776.0, 302.0, topRight: radius, bottomRight: radius), color: sliderTheme.disabledInactiveTrackColor),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Default slider track draws correctly', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
@ -314,7 +240,12 @@ void main() {
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false));
|
||||
await tester.pumpAndSettle(); // wait for disable animation
|
||||
|
||||
// The disabled slider thumb is the same size as the enabled thumb.
|
||||
// The disabled slider thumb has a horizontal gap between itself and the
|
||||
// track segments. Therefore, the track segments are shorter since they do
|
||||
// not extend to the center of the thumb, but rather the outer edge of th
|
||||
// gap. As a result, the `right` value of the first segment is less than it
|
||||
// is above, and the `left` value of the second segment is more than it is
|
||||
// above.
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
@ -428,38 +359,32 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Slider V2 value indicator shape draws correctly', (WidgetTester tester) async {
|
||||
testWidgets('Default slider value indicator shape draws correctly', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
primarySwatch: Colors.blue,
|
||||
);
|
||||
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(
|
||||
thumbColor: Colors.red.shade500,
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
);
|
||||
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500, showValueIndicator: ShowValueIndicator.always);
|
||||
Widget buildApp(String value, { double sliderValue = 0.5, double textScale = 1.0 }) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScale),
|
||||
child: Material(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: SliderTheme(
|
||||
data: sliderTheme,
|
||||
child: Slider(
|
||||
value: sliderValue,
|
||||
label: value,
|
||||
divisions: 3,
|
||||
onChanged: (double d) { },
|
||||
useV2Slider: true,
|
||||
),
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScale),
|
||||
child: Material(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: SliderTheme(
|
||||
data: sliderTheme,
|
||||
child: Slider(
|
||||
value: sliderValue,
|
||||
label: value,
|
||||
divisions: 3,
|
||||
onChanged: (double d) { },
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -468,195 +393,14 @@ void main() {
|
||||
|
||||
await tester.pumpWidget(buildApp('1'));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.firstRenderObject(find.byType(Overlay));
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
Offset center = tester.getCenter(find.byType(Slider));
|
||||
TestGesture gesture = await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..path(
|
||||
includes: const <Offset>[
|
||||
Offset(0.0, 0.0),
|
||||
Offset(-20.0, -12.0),
|
||||
Offset(20.0, -34.0),
|
||||
Offset(0.0, -38.0),
|
||||
],
|
||||
color: const Color(0xf55f5f5f),
|
||||
),
|
||||
);
|
||||
|
||||
await gesture.up();
|
||||
|
||||
// Test that it expands with a larger label.
|
||||
await tester.pumpWidget(buildApp('1000'));
|
||||
center = tester.getCenter(find.byType(Slider));
|
||||
gesture = await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect()
|
||||
..rrect()
|
||||
..path(
|
||||
includes: const <Offset>[
|
||||
Offset(0.0, 0.0),
|
||||
Offset(-30.0, -12.0),
|
||||
Offset(30.0, -34.0),
|
||||
Offset(0.0, -38.0),
|
||||
],
|
||||
color: const Color(0xf55f5f5f),
|
||||
),
|
||||
);
|
||||
await gesture.up();
|
||||
|
||||
// Test that it avoids the left edge of the screen.
|
||||
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0));
|
||||
center = tester.getCenter(find.byType(Slider));
|
||||
gesture = await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect()
|
||||
..rrect()
|
||||
..path(
|
||||
includes: const <Offset>[
|
||||
Offset(0.0, 0.0),
|
||||
Offset(-12.0, -12.0),
|
||||
Offset(110.0, -34.0),
|
||||
Offset(0.0, -38.0),
|
||||
],
|
||||
color: const Color(0xf55f5f5f),
|
||||
)
|
||||
);
|
||||
await gesture.up();
|
||||
|
||||
// Test that it avoids the right edge of the screen.
|
||||
await tester.pumpWidget(buildApp('1000000', sliderValue: 1.0));
|
||||
center = tester.getCenter(find.byType(Slider));
|
||||
gesture = await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect()
|
||||
..rrect()
|
||||
..path(
|
||||
includes: const <Offset>[
|
||||
Offset(0.0, 0.0),
|
||||
Offset(-110.0, -12.0),
|
||||
Offset(12.0, -34.0),
|
||||
Offset(0.0, -38.0),
|
||||
],
|
||||
color: const Color(0xf55f5f5f),
|
||||
)
|
||||
);
|
||||
await gesture.up();
|
||||
|
||||
// Test that the box decreases in height when the text scale gets smaller.
|
||||
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0, textScale: 0.5));
|
||||
center = tester.getCenter(find.byType(Slider));
|
||||
gesture = await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect()
|
||||
..rrect()
|
||||
..path(
|
||||
includes: const <Offset>[
|
||||
Offset(0.0, 0.0),
|
||||
Offset(-12.0, -12.0),
|
||||
Offset(61.0, -16.0),
|
||||
Offset(0.0, -20.0),
|
||||
],
|
||||
excludes: const <Offset>[
|
||||
Offset(0.0, -38.0)
|
||||
],
|
||||
color: const Color(0xf55f5f5f),
|
||||
)
|
||||
);
|
||||
await gesture.up();
|
||||
|
||||
// Test that the box increases in height when the text scale gets bigger.
|
||||
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0, textScale: 2.0));
|
||||
center = tester.getCenter(find.byType(Slider));
|
||||
gesture = await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
paints
|
||||
..rrect()
|
||||
..rrect()
|
||||
..path(
|
||||
includes: const <Offset>[
|
||||
Offset(0.0, 0.0),
|
||||
Offset(-12.0, -16.0),
|
||||
Offset(208.0, -40.0),
|
||||
Offset(0.0, -50.0),
|
||||
],
|
||||
color: const Color(0xf55f5f5f),
|
||||
)
|
||||
);
|
||||
await gesture.up();
|
||||
}, skip: isBrowser);
|
||||
|
||||
testWidgets('Default paddle slider value indicator shape draws correctly', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
primarySwatch: Colors.blue,
|
||||
);
|
||||
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(
|
||||
thumbColor: Colors.red.shade500,
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
valueIndicatorShape: const PaddleSliderValueIndicatorShape(),
|
||||
);
|
||||
Widget buildApp(String value, { double sliderValue = 0.5, double textScale = 1.0 }) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScale),
|
||||
child: Material(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: SliderTheme(
|
||||
data: sliderTheme,
|
||||
child: Slider(
|
||||
value: sliderValue,
|
||||
label: value,
|
||||
divisions: 3,
|
||||
onChanged: (double d) { },
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildApp('1'));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.firstRenderObject(find.byType(Overlay));
|
||||
|
||||
Offset center = tester.getCenter(find.byType(Slider));
|
||||
TestGesture gesture = await tester.startGesture(center);
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
sliderBox,
|
||||
paints
|
||||
..path(
|
||||
color: sliderTheme.valueIndicatorColor,
|
||||
@ -678,7 +422,7 @@ void main() {
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
sliderBox,
|
||||
paints
|
||||
..path(
|
||||
color: sliderTheme.valueIndicatorColor,
|
||||
@ -699,7 +443,7 @@ void main() {
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
sliderBox,
|
||||
paints
|
||||
..path(
|
||||
color: sliderTheme.valueIndicatorColor,
|
||||
@ -720,7 +464,7 @@ void main() {
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
sliderBox,
|
||||
paints
|
||||
..path(
|
||||
color: sliderTheme.valueIndicatorColor,
|
||||
@ -741,7 +485,7 @@ void main() {
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
sliderBox,
|
||||
paints
|
||||
..path(
|
||||
color: sliderTheme.valueIndicatorColor,
|
||||
@ -767,7 +511,7 @@ void main() {
|
||||
// Wait for value indicator animation to finish.
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
valueIndicatorBox,
|
||||
sliderBox,
|
||||
paints
|
||||
..path(
|
||||
color: sliderTheme.valueIndicatorColor,
|
||||
@ -814,36 +558,6 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('The slider V2 track height can be overridden', (WidgetTester tester) async {
|
||||
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(trackHeight: 16);
|
||||
const Radius radius = Radius.circular(8);
|
||||
const Radius activatedRadius = Radius.circular(9);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, useV2Slider: true));
|
||||
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
// Top and bottom are centerY (300) + and - trackRadius (8).
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 291.0, 212.0, 309.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.activeTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 292.0, 776.0, 308.0, topRight: radius, bottomRight: radius), color: sliderTheme.inactiveTrackColor),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.25, enabled: false, useV2Slider: true));
|
||||
await tester.pumpAndSettle(); // wait for disable animation
|
||||
|
||||
// The disabled thumb is smaller so the active track has to paint longer to
|
||||
// get to the edge.
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(24.0, 291.0, 212.0, 309.0, topLeft: activatedRadius, bottomLeft: activatedRadius), color: sliderTheme.disabledActiveTrackColor)
|
||||
..rrect(rrect: RRect.fromLTRBAndCorners(212.0, 292.0, 776.0, 308.0, topRight: radius, bottomRight: radius), color: sliderTheme.disabledInactiveTrackColor),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('The default slider thumb shape sizes can be overridden', (WidgetTester tester) async {
|
||||
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||
thumbShape: const RoundSliderThumbShape(
|
||||
@ -892,6 +606,7 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
testWidgets('The default slider tick mark shape size can be overridden', (WidgetTester tester) async {
|
||||
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||
tickMarkShape: const RoundSliderTickMarkShape(tickMarkRadius: 5),
|
||||
@ -913,7 +628,7 @@ void main() {
|
||||
..circle(x: 771, y: 300, radius: 5, color: sliderTheme.inactiveTickMarkColor),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2, enabled: false));
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2, enabled: false));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
@ -925,39 +640,6 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('The default slider V2 tick mark shape size can be overridden', (WidgetTester tester) async {
|
||||
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||
tickMarkShape: const RoundSliderTickMarkShape(tickMarkRadius: 5, useV2Slider: true),
|
||||
activeTickMarkColor: const Color(0xfadedead),
|
||||
inactiveTickMarkColor: const Color(0xfadebeef),
|
||||
disabledActiveTickMarkColor: const Color(0xfadecafe),
|
||||
disabledInactiveTickMarkColor: const Color(0xfadeface),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2, useV2Slider: true));
|
||||
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..circle(x: 26, y: 300, radius: 5, color: sliderTheme.activeTickMarkColor)
|
||||
..circle(x: 400, y: 300, radius: 5, color: sliderTheme.activeTickMarkColor)
|
||||
..circle(x: 774, y: 300, radius: 5, color: sliderTheme.inactiveTickMarkColor),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(sliderTheme, value: 0.5, divisions: 2, enabled: false, useV2Slider: true));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
sliderBox,
|
||||
paints
|
||||
..circle(x: 26, y: 300, radius: 5, color: sliderTheme.disabledActiveTickMarkColor)
|
||||
..circle(x: 400, y: 300, radius: 5, color: sliderTheme.disabledActiveTickMarkColor)
|
||||
..circle(x: 774, y: 300, radius: 5, color: sliderTheme.disabledInactiveTickMarkColor),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('The default slider overlay shape size can be overridden', (WidgetTester tester) async {
|
||||
const double uniqueOverlayRadius = 23;
|
||||
final SliderThemeData sliderTheme = ThemeData().sliderTheme.copyWith(
|
||||
@ -1118,7 +800,7 @@ void main() {
|
||||
divisions: 4,
|
||||
));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.firstRenderObject(find.byType(Overlay));
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
// Tap the center of the track and wait for animations to finish.
|
||||
final Offset center = tester.getCenter(find.byType(Slider));
|
||||
@ -1126,132 +808,41 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Only 1 value indicator.
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawRect, 0));
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawCircle, 0));
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 1));
|
||||
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
testWidgets('PaddleSliderValueIndicatorShape skips all painting at zero scale', (WidgetTester tester) async {
|
||||
// Pump a slider with just a value indicator.
|
||||
await tester.pumpWidget(_buildApp(
|
||||
ThemeData().sliderTheme.copyWith(
|
||||
trackHeight: 0,
|
||||
overlayShape: SliderComponentShape.noOverlay,
|
||||
thumbShape: SliderComponentShape.noThumb,
|
||||
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
valueIndicatorShape: const PaddleSliderValueIndicatorShape(),
|
||||
),
|
||||
value: 0.5,
|
||||
divisions: 4,
|
||||
));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.firstRenderObject(find.byType(Overlay));
|
||||
|
||||
// Tap the center of the track to kick off the animation of the value indicator.
|
||||
final Offset center = tester.getCenter(find.byType(Slider));
|
||||
final TestGesture gesture = await tester.startGesture(center);
|
||||
|
||||
// Nothing to paint at scale 0.
|
||||
await tester.pump();
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||
|
||||
// Painting a path for the value indicator.
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 1));
|
||||
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
testWidgets('Default slider value indicator shape skips all painting at zero scale', (WidgetTester tester) async {
|
||||
// Pump a slider with just a value indicator.
|
||||
await tester.pumpWidget(_buildApp(
|
||||
ThemeData().sliderTheme.copyWith(
|
||||
trackHeight: 0,
|
||||
overlayShape: SliderComponentShape.noOverlay,
|
||||
thumbShape: SliderComponentShape.noThumb,
|
||||
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
),
|
||||
value: 0.5,
|
||||
divisions: 4,
|
||||
));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.firstRenderObject(find.byType(Overlay));
|
||||
|
||||
// Tap the center of the track to kick off the animation of the value indicator.
|
||||
final Offset center = tester.getCenter(find.byType(Slider));
|
||||
final TestGesture gesture = await tester.startGesture(center);
|
||||
|
||||
// Nothing to paint at scale 0.
|
||||
await tester.pump();
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||
|
||||
// Painting a path for the value indicator.
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 1));
|
||||
expect(sliderBox, paintsExactlyCountTimes(#drawRect, 0));
|
||||
expect(sliderBox, paintsExactlyCountTimes(#drawCircle, 0));
|
||||
expect(sliderBox, paintsExactlyCountTimes(#drawPath, 1));
|
||||
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
testWidgets('PaddleRangeSliderValueIndicatorShape skips all painting at zero scale', (WidgetTester tester) async {
|
||||
// Pump a slider with just a value indicator.
|
||||
await tester.pumpWidget(_buildRangeApp(
|
||||
ThemeData().sliderTheme.copyWith(
|
||||
trackHeight: 0,
|
||||
rangeValueIndicatorShape: const PaddleRangeSliderValueIndicatorShape(),
|
||||
),
|
||||
values: const RangeValues(0, 0.5),
|
||||
divisions: 4,
|
||||
));
|
||||
|
||||
// final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(RangeSlider));
|
||||
final RenderBox valueIndicatorBox = tester.firstRenderObject(find.byType(Overlay));
|
||||
|
||||
// Tap the center of the track to kick off the animation of the value indicator.
|
||||
final Offset center = tester.getCenter(find.byType(RangeSlider));
|
||||
final TestGesture gesture = await tester.startGesture(center);
|
||||
|
||||
// No value indicator path to paint at scale 0.
|
||||
await tester.pump();
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||
|
||||
// Painting a path for each value indicator.
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
|
||||
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
testWidgets('Default range indicator shape skips all painting at zero scale', (WidgetTester tester) async {
|
||||
// Pump a slider with just a value indicator.
|
||||
await tester.pumpWidget(_buildRangeApp(
|
||||
await tester.pumpWidget(_buildApp(
|
||||
ThemeData().sliderTheme.copyWith(
|
||||
trackHeight: 0,
|
||||
overlayShape: SliderComponentShape.noOverlay,
|
||||
thumbShape: SliderComponentShape.noThumb,
|
||||
tickMarkShape: SliderTickMarkShape.noTickMark,
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
rangeValueIndicatorShape: const PaddleRangeSliderValueIndicatorShape(),
|
||||
),
|
||||
values: const RangeValues(0, 0.5),
|
||||
value: 0.5,
|
||||
divisions: 4,
|
||||
));
|
||||
|
||||
final RenderBox valueIndicatorBox = tester.firstRenderObject(find.byType(Overlay));
|
||||
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
|
||||
|
||||
// Tap the center of the track to kick off the animation of the value indicator.
|
||||
final Offset center = tester.getCenter(find.byType(RangeSlider));
|
||||
final Offset center = tester.getCenter(find.byType(Slider));
|
||||
final TestGesture gesture = await tester.startGesture(center);
|
||||
|
||||
// No value indicator path to paint at scale 0.
|
||||
// Nothing to paint at scale 0.
|
||||
await tester.pump();
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 0));
|
||||
expect(sliderBox, paintsNothing);
|
||||
|
||||
// Painting a path for each value indicator.
|
||||
// Painting a path for the value indicator.
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
|
||||
expect(sliderBox, paintsExactlyCountTimes(#drawPath, 1));
|
||||
|
||||
await gesture.up();
|
||||
});
|
||||
@ -1262,7 +853,6 @@ Widget _buildApp(
|
||||
double value = 0.0,
|
||||
bool enabled = true,
|
||||
int divisions,
|
||||
bool useV2Slider = false,
|
||||
}) {
|
||||
final ValueChanged<double> onChanged = enabled ? (double d) => value = d : null;
|
||||
return MaterialApp(
|
||||
@ -1275,31 +865,6 @@ Widget _buildApp(
|
||||
label: '$value',
|
||||
onChanged: onChanged,
|
||||
divisions: divisions,
|
||||
useV2Slider: useV2Slider
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRangeApp(
|
||||
SliderThemeData sliderTheme, {
|
||||
RangeValues values = const RangeValues(0, 0),
|
||||
bool enabled = true,
|
||||
int divisions,
|
||||
}) {
|
||||
final ValueChanged<RangeValues> onChanged = enabled ? (RangeValues d) => values = d : null;
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: SliderTheme(
|
||||
data: sliderTheme,
|
||||
child: RangeSlider(
|
||||
values: values,
|
||||
labels: RangeLabels(values.start.toString(), values.end.toString()),
|
||||
onChanged: onChanged,
|
||||
divisions: divisions,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -169,10 +169,7 @@ void main() {
|
||||
(ByteData data) { },
|
||||
);
|
||||
final RenderObject renderObject = tester.renderObject(find.byType(RangeSlider));
|
||||
|
||||
bool sliderBoxNeedsLayout;
|
||||
renderObject.visitChildren((RenderObject child) {sliderBoxNeedsLayout = child.debugNeedsLayout;});
|
||||
expect(sliderBoxNeedsLayout, isTrue);
|
||||
expect(renderObject.debugNeedsLayout, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('Slider relayout upon system fonts changes', (WidgetTester tester) async {
|
||||
@ -194,11 +191,8 @@ void main() {
|
||||
SystemChannels.system.codec.encodeMessage(data),
|
||||
(ByteData data) { },
|
||||
);
|
||||
final RenderObject renderObject = tester.renderObject(find.byType(Slider));
|
||||
|
||||
bool sliderBoxNeedsLayout;
|
||||
renderObject.visitChildren((RenderObject child) {sliderBoxNeedsLayout = child.debugNeedsLayout;});
|
||||
expect(sliderBoxNeedsLayout, isTrue);
|
||||
final RenderObject renderObject = tester.renderObject(find.byType(Slider));
|
||||
expect(renderObject.debugNeedsLayout, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('TimePicker relayout upon system fonts changes', (WidgetTester tester) async {
|
||||
|
Loading…
Reference in New Issue
Block a user