Fix a Slider widget accessibility bug (#102129)

This commit is contained in:
xubaolin 2022-04-26 09:24:07 +08:00 committed by GitHub
parent 63574cf76a
commit 2381657394
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 7 deletions

View file

@ -285,7 +285,9 @@ class RangeSlider extends StatefulWidget {
/// If null, the slider is continuous.
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.
///
@ -1491,7 +1493,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
double value,
double increasedValue,
double decreasedValue,
String? label,
VoidCallback increaseAction,
VoidCallback decreaseAction,
) {
@ -1503,7 +1504,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
config.onIncrease = increaseAction;
config.onDecrease = decreaseAction;
}
config.label = label ?? '';
if (semanticFormatterCallback != null) {
config.value = semanticFormatterCallback!(_state._lerp(value));
config.increasedValue = semanticFormatterCallback!(_state._lerp(increasedValue));
@ -1529,7 +1530,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
values.start,
_increasedStartValue,
_decreasedStartValue,
labels?.start,
_increaseStartAction,
_decreaseStartAction,
);
@ -1537,7 +1537,6 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
values.end,
_increasedEndValue,
_decreasedEndValue,
labels?.end,
_increaseEndAction,
_decreaseEndAction,
);

View file

@ -317,7 +317,8 @@ class Slider extends StatefulWidget {
/// If null, the slider is continuous.
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
/// as part of the value indicator shape.
@ -1506,7 +1507,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
config.onIncrease = increaseAction;
config.onDecrease = decreaseAction;
}
config.label = _label ?? '';
if (semanticFormatterCallback != null) {
config.value = semanticFormatterCallback!(_state._lerp(value));
config.increasedValue = semanticFormatterCallback!(_state._lerp((value + _semanticActionUnit).clamp(0.0, 1.0)));

View file

@ -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 {
await tester.pumpWidget(
MaterialApp(

View file

@ -1816,6 +1816,66 @@ void main() {
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 {
final FocusNode focusNode = FocusNode(debugLabel: 'Slider');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;