Unverified Commit 23816573 authored by xubaolin's avatar xubaolin Committed by GitHub

Fix a `Slider` widget accessibility bug (#102129)

parent 63574cf7
...@@ -285,7 +285,9 @@ class RangeSlider extends StatefulWidget { ...@@ -285,7 +285,9 @@ class RangeSlider extends StatefulWidget {
/// If null, the slider is continuous. /// If null, the slider is continuous.
final int? divisions; final int? divisions;
/// Labels to show as text in the [SliderThemeData.rangeValueIndicatorShape]. /// Labels to show as text in the [SliderThemeData.rangeValueIndicatorShape]
/// when the slider is active and [SliderThemeData.showValueIndicator]
/// is satisfied.
/// ///
/// There are two labels: one for the start thumb and one for the end thumb. /// There are two labels: one for the start thumb and one for the end thumb.
/// ///
...@@ -1491,7 +1493,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix ...@@ -1491,7 +1493,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
double value, double value,
double increasedValue, double increasedValue,
double decreasedValue, double decreasedValue,
String? label,
VoidCallback increaseAction, VoidCallback increaseAction,
VoidCallback decreaseAction, VoidCallback decreaseAction,
) { ) {
...@@ -1503,7 +1504,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix ...@@ -1503,7 +1504,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
config.onIncrease = increaseAction; config.onIncrease = increaseAction;
config.onDecrease = decreaseAction; config.onDecrease = decreaseAction;
} }
config.label = label ?? '';
if (semanticFormatterCallback != null) { if (semanticFormatterCallback != null) {
config.value = semanticFormatterCallback!(_state._lerp(value)); config.value = semanticFormatterCallback!(_state._lerp(value));
config.increasedValue = semanticFormatterCallback!(_state._lerp(increasedValue)); config.increasedValue = semanticFormatterCallback!(_state._lerp(increasedValue));
...@@ -1529,7 +1530,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix ...@@ -1529,7 +1530,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
values.start, values.start,
_increasedStartValue, _increasedStartValue,
_decreasedStartValue, _decreasedStartValue,
labels?.start,
_increaseStartAction, _increaseStartAction,
_decreaseStartAction, _decreaseStartAction,
); );
...@@ -1537,7 +1537,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix ...@@ -1537,7 +1537,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
values.end, values.end,
_increasedEndValue, _increasedEndValue,
_decreasedEndValue, _decreasedEndValue,
labels?.end,
_increaseEndAction, _increaseEndAction,
_decreaseEndAction, _decreaseEndAction,
); );
......
...@@ -317,7 +317,8 @@ class Slider extends StatefulWidget { ...@@ -317,7 +317,8 @@ class Slider extends StatefulWidget {
/// If null, the slider is continuous. /// If null, the slider is continuous.
final int? divisions; final int? divisions;
/// A label to show above the slider when the slider is active. /// A label to show above the slider when the slider is active and
/// [SliderThemeData.showValueIndicator] is satisfied.
/// ///
/// It is used to display the value of a discrete slider, and it is displayed /// It is used to display the value of a discrete slider, and it is displayed
/// as part of the value indicator shape. /// as part of the value indicator shape.
...@@ -1506,7 +1507,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1506,7 +1507,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
config.onIncrease = increaseAction; config.onIncrease = increaseAction;
config.onDecrease = decreaseAction; config.onDecrease = decreaseAction;
} }
config.label = _label ?? '';
if (semanticFormatterCallback != null) { if (semanticFormatterCallback != null) {
config.value = semanticFormatterCallback!(_state._lerp(value)); config.value = semanticFormatterCallback!(_state._lerp(value));
config.increasedValue = semanticFormatterCallback!(_state._lerp((value + _semanticActionUnit).clamp(0.0, 1.0))); config.increasedValue = semanticFormatterCallback!(_state._lerp((value + _semanticActionUnit).clamp(0.0, 1.0)));
......
...@@ -1681,6 +1681,65 @@ void main() { ...@@ -1681,6 +1681,65 @@ void main() {
); );
}); });
// Regression test for https://github.com/flutter/flutter/issues/101868
testWidgets('RangeSlider.label info should not write to semantic node', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Theme(
data: ThemeData.light(),
child: Directionality(
textDirection: TextDirection.ltr,
child: Material(
child: RangeSlider(
values: const RangeValues(10.0, 12.0),
max: 100.0,
onChanged: (RangeValues v) { },
labels: const RangeLabels('Begin', 'End'),
),
),
),
),
),
);
await tester.pumpAndSettle();
expect(
tester.getSemantics(find.byType(RangeSlider)),
matchesSemantics(
scopesRoute: true,
children:<Matcher>[
matchesSemantics(
children: <Matcher>[
matchesSemantics(
isEnabled: true,
isSlider: true,
hasEnabledState: true,
hasIncreaseAction: true,
hasDecreaseAction: true,
value: '10%',
increasedValue: '10%',
decreasedValue: '5%',
label: ''
),
matchesSemantics(
isEnabled: true,
isSlider: true,
hasEnabledState: true,
hasIncreaseAction: true,
hasDecreaseAction: true,
value: '12%',
increasedValue: '17%',
decreasedValue: '12%',
label: ''
),
],
),
],
),
);
});
testWidgets('Range Slider Semantics', (WidgetTester tester) async { testWidgets('Range Slider Semantics', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
......
...@@ -1816,6 +1816,66 @@ void main() { ...@@ -1816,6 +1816,66 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
// Regression test for https://github.com/flutter/flutter/issues/101868
testWidgets('Slider.label info should not write to semantic node', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(MaterialApp(
home: Directionality(
textDirection: TextDirection.ltr,
child: Material(
child: Slider(
value: 40.0,
max: 200.0,
divisions: 10,
semanticFormatterCallback: (double value) => value.round().toString(),
onChanged: (double v) { },
label: 'Bingo',
),
),
),
));
expect(
semantics,
hasSemantics(
TestSemantics.root(
children: <TestSemantics>[
TestSemantics(
id: 1,
textDirection: TextDirection.ltr,
children: <TestSemantics>[
TestSemantics(
id: 2,
children: <TestSemantics>[
TestSemantics(
id: 3,
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
TestSemantics(
id: 4,
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider],
actions: <SemanticsAction>[SemanticsAction.increase, SemanticsAction.decrease],
value: '40',
increasedValue: '60',
decreasedValue: '20',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
),
);
semantics.dispose();
});
testWidgets('Slider is focusable and has correct focus color', (WidgetTester tester) async { testWidgets('Slider is focusable and has correct focus color', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Slider'); final FocusNode focusNode = FocusNode(debugLabel: 'Slider');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment