Unverified Commit 8473da22 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Fix `Slider` semantic node size (#115285)

parent 333397a0
...@@ -821,22 +821,11 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin { ...@@ -821,22 +821,11 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
// in range_slider.dart. // in range_slider.dart.
Size screenSize() => MediaQuery.of(context).size; Size screenSize() => MediaQuery.of(context).size;
VoidCallback? handleDidGainAccessibilityFocus; void handleDidGainAccessibilityFocus() {
switch (theme.platform) { // Automatically activate the slider when it receives a11y focus.
case TargetPlatform.android: if (!focusNode.hasFocus && focusNode.canRequestFocus) {
case TargetPlatform.fuchsia: focusNode.requestFocus();
case TargetPlatform.iOS: }
case TargetPlatform.linux:
case TargetPlatform.macOS:
break;
case TargetPlatform.windows:
handleDidGainAccessibilityFocus = () {
// Automatically activate the slider when it receives a11y focus.
if (!focusNode.hasFocus && focusNode.canRequestFocus) {
focusNode.requestFocus();
}
};
break;
} }
final Map<ShortcutActivator, Intent> shortcutMap; final Map<ShortcutActivator, Intent> shortcutMap;
...@@ -857,38 +846,35 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin { ...@@ -857,38 +846,35 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
? math.min(MediaQuery.of(context).textScaleFactor, 1.3) ? math.min(MediaQuery.of(context).textScaleFactor, 1.3)
: MediaQuery.of(context).textScaleFactor; : MediaQuery.of(context).textScaleFactor;
return Semantics( return FocusableActionDetector(
container: true, actions: _actionMap,
slider: true, shortcuts: shortcutMap,
onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus, focusNode: focusNode,
child: FocusableActionDetector( autofocus: widget.autofocus,
actions: _actionMap, enabled: _enabled,
shortcuts: shortcutMap, onShowFocusHighlight: _handleFocusHighlightChanged,
focusNode: focusNode, onShowHoverHighlight: _handleHoverChanged,
autofocus: widget.autofocus, mouseCursor: effectiveMouseCursor,
enabled: _enabled, includeFocusSemantics: false,
onShowFocusHighlight: _handleFocusHighlightChanged, child: CompositedTransformTarget(
onShowHoverHighlight: _handleHoverChanged, link: _layerLink,
mouseCursor: effectiveMouseCursor, child: _SliderRenderObjectWidget(
child: CompositedTransformTarget( key: _renderObjectKey,
link: _layerLink, value: _convert(widget.value),
child: _SliderRenderObjectWidget( secondaryTrackValue: (widget.secondaryTrackValue != null) ? _convert(widget.secondaryTrackValue!) : null,
key: _renderObjectKey, divisions: widget.divisions,
value: _convert(widget.value), label: widget.label,
secondaryTrackValue: (widget.secondaryTrackValue != null) ? _convert(widget.secondaryTrackValue!) : null, sliderTheme: sliderTheme,
divisions: widget.divisions, textScaleFactor: textScaleFactor,
label: widget.label, screenSize: screenSize(),
sliderTheme: sliderTheme, onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
textScaleFactor: textScaleFactor, onChangeStart: _handleDragStart,
screenSize: screenSize(), onChangeEnd: _handleDragEnd,
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null, state: this,
onChangeStart: _handleDragStart, semanticFormatterCallback: widget.semanticFormatterCallback,
onChangeEnd: _handleDragEnd, onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
state: this, hasFocus: _focused,
semanticFormatterCallback: widget.semanticFormatterCallback, hovering: _hovering,
hasFocus: _focused,
hovering: _hovering,
),
), ),
), ),
); );
...@@ -949,6 +935,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { ...@@ -949,6 +935,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
required this.onChangeEnd, required this.onChangeEnd,
required this.state, required this.state,
required this.semanticFormatterCallback, required this.semanticFormatterCallback,
required this.onDidGainAccessibilityFocus,
required this.hasFocus, required this.hasFocus,
required this.hovering, required this.hovering,
}); });
...@@ -964,6 +951,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { ...@@ -964,6 +951,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
final ValueChanged<double>? onChangeStart; final ValueChanged<double>? onChangeStart;
final ValueChanged<double>? onChangeEnd; final ValueChanged<double>? onChangeEnd;
final SemanticFormatterCallback? semanticFormatterCallback; final SemanticFormatterCallback? semanticFormatterCallback;
final VoidCallback? onDidGainAccessibilityFocus;
final _SliderState state; final _SliderState state;
final bool hasFocus; final bool hasFocus;
final bool hovering; final bool hovering;
...@@ -984,6 +972,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { ...@@ -984,6 +972,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
state: state, state: state,
textDirection: Directionality.of(context), textDirection: Directionality.of(context),
semanticFormatterCallback: semanticFormatterCallback, semanticFormatterCallback: semanticFormatterCallback,
onDidGainAccessibilityFocus: onDidGainAccessibilityFocus,
platform: Theme.of(context).platform, platform: Theme.of(context).platform,
hasFocus: hasFocus, hasFocus: hasFocus,
hovering: hovering, hovering: hovering,
...@@ -1008,6 +997,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { ...@@ -1008,6 +997,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
..onChangeEnd = onChangeEnd ..onChangeEnd = onChangeEnd
..textDirection = Directionality.of(context) ..textDirection = Directionality.of(context)
..semanticFormatterCallback = semanticFormatterCallback ..semanticFormatterCallback = semanticFormatterCallback
..onDidGainAccessibilityFocus = onDidGainAccessibilityFocus
..platform = Theme.of(context).platform ..platform = Theme.of(context).platform
..hasFocus = hasFocus ..hasFocus = hasFocus
..hovering = hovering ..hovering = hovering
...@@ -1029,6 +1019,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1029,6 +1019,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
required TargetPlatform platform, required TargetPlatform platform,
required ValueChanged<double>? onChanged, required ValueChanged<double>? onChanged,
required SemanticFormatterCallback? semanticFormatterCallback, required SemanticFormatterCallback? semanticFormatterCallback,
required this.onDidGainAccessibilityFocus,
required this.onChangeStart, required this.onChangeStart,
required this.onChangeEnd, required this.onChangeEnd,
required _SliderState state, required _SliderState state,
...@@ -1114,6 +1105,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1114,6 +1105,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
bool _active = false; bool _active = false;
double _currentDragValue = 0.0; double _currentDragValue = 0.0;
Rect? overlayRect; Rect? overlayRect;
late Offset _thumbCenter;
// This rect is used in gesture calculations, where the gesture coordinates // This rect is used in gesture calculations, where the gesture coordinates
// are relative to the sliders origin. Therefore, the offset is passed as // are relative to the sliders origin. Therefore, the offset is passed as
...@@ -1259,6 +1251,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1259,6 +1251,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
} }
} }
VoidCallback? onDidGainAccessibilityFocus;
ValueChanged<double>? onChangeStart; ValueChanged<double>? onChangeStart;
ValueChanged<double>? onChangeEnd; ValueChanged<double>? onChangeEnd;
...@@ -1582,10 +1575,10 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1582,10 +1575,10 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
sliderTheme: _sliderTheme, sliderTheme: _sliderTheme,
isDiscrete: isDiscrete, isDiscrete: isDiscrete,
); );
final Offset thumbCenter = Offset(trackRect.left + visualPosition * trackRect.width, trackRect.center.dy); _thumbCenter = Offset(trackRect.left + visualPosition * trackRect.width, trackRect.center.dy);
if (isInteractive) { if (isInteractive) {
final Size overlaySize = sliderTheme.overlayShape!.getPreferredSize(isInteractive, false); final Size overlaySize = sliderTheme.overlayShape!.getPreferredSize(isInteractive, false);
overlayRect = Rect.fromCircle(center: thumbCenter, radius: overlaySize.width / 2.0); overlayRect = Rect.fromCircle(center: _thumbCenter, radius: overlaySize.width / 2.0);
} }
final Offset? secondaryOffset = (secondaryVisualPosition != null) ? Offset(trackRect.left + secondaryVisualPosition * trackRect.width, trackRect.center.dy) : null; final Offset? secondaryOffset = (secondaryVisualPosition != null) ? Offset(trackRect.left + secondaryVisualPosition * trackRect.width, trackRect.center.dy) : null;
...@@ -1596,7 +1589,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1596,7 +1589,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
sliderTheme: _sliderTheme, sliderTheme: _sliderTheme,
enableAnimation: _enableAnimation, enableAnimation: _enableAnimation,
textDirection: _textDirection, textDirection: _textDirection,
thumbCenter: thumbCenter, thumbCenter: _thumbCenter,
secondaryOffset: secondaryOffset, secondaryOffset: secondaryOffset,
isDiscrete: isDiscrete, isDiscrete: isDiscrete,
isEnabled: isInteractive, isEnabled: isInteractive,
...@@ -1605,7 +1598,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1605,7 +1598,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
if (!_overlayAnimation.isDismissed) { if (!_overlayAnimation.isDismissed) {
_sliderTheme.overlayShape!.paint( _sliderTheme.overlayShape!.paint(
context, context,
thumbCenter, _thumbCenter,
activationAnimation: _overlayAnimation, activationAnimation: _overlayAnimation,
enableAnimation: _enableAnimation, enableAnimation: _enableAnimation,
isDiscrete: isDiscrete, isDiscrete: isDiscrete,
...@@ -1642,7 +1635,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1642,7 +1635,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
sliderTheme: _sliderTheme, sliderTheme: _sliderTheme,
enableAnimation: _enableAnimation, enableAnimation: _enableAnimation,
textDirection: _textDirection, textDirection: _textDirection,
thumbCenter: thumbCenter, thumbCenter: _thumbCenter,
isEnabled: isInteractive, isEnabled: isInteractive,
); );
} }
...@@ -1655,7 +1648,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1655,7 +1648,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
if (attached) { if (attached) {
_sliderTheme.valueIndicatorShape!.paint( _sliderTheme.valueIndicatorShape!.paint(
context, context,
offset + thumbCenter, offset + _thumbCenter,
activationAnimation: _valueIndicatorAnimation, activationAnimation: _valueIndicatorAnimation,
enableAnimation: _enableAnimation, enableAnimation: _enableAnimation,
isDiscrete: isDiscrete, isDiscrete: isDiscrete,
...@@ -1674,7 +1667,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1674,7 +1667,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
_sliderTheme.thumbShape!.paint( _sliderTheme.thumbShape!.paint(
context, context,
thumbCenter, _thumbCenter,
activationAnimation: _overlayAnimation, activationAnimation: _overlayAnimation,
enableAnimation: _enableAnimation, enableAnimation: _enableAnimation,
isDiscrete: isDiscrete, isDiscrete: isDiscrete,
...@@ -1688,12 +1681,23 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1688,12 +1681,23 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
); );
} }
@override
void assembleSemanticsNode(SemanticsNode node, SemanticsConfiguration config, Iterable<SemanticsNode> children) {
node.rect = Rect.fromCenter(
center: _thumbCenter,
width: kMinInteractiveDimension,
height: kMinInteractiveDimension,
);
node.updateWith(config: config);
}
@override @override
void describeSemanticsConfiguration(SemanticsConfiguration config) { void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config); super.describeSemanticsConfiguration(config);
// The Slider widget has its own Focus widget with semantics information, // The Slider widget has its own Focus widget with semantics information,
// and we want that semantics node to collect the semantics information here // and want that semantics node to collect the semantics information here
// so that it's all in the same node: otherwise Talkback sees that the node // so that it's all in the same node: otherwise Talkback sees that the node
// has focusable children, and it won't focus the Slider's Focus widget // has focusable children, and it won't focus the Slider's Focus widget
// because it thinks the Focus widget's node doesn't have anything to say // because it thinks the Focus widget's node doesn't have anything to say
...@@ -1701,9 +1705,23 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1701,9 +1705,23 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
// information into one node means that Talkback will recognize that it has // information into one node means that Talkback will recognize that it has
// something to say and focus it when it receives keyboard focus. // something to say and focus it when it receives keyboard focus.
// (See https://github.com/flutter/flutter/issues/57038 for context). // (See https://github.com/flutter/flutter/issues/57038 for context).
config.isSemanticBoundary = false; config.isSemanticBoundary = true;
config.isEnabled = isInteractive; config.isEnabled = isInteractive;
config.isSlider = true;
config.isFocusable = isInteractive;
config.isFocused = hasFocus;
switch (_platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.iOS:
case TargetPlatform.linux:
case TargetPlatform.macOS:
break;
case TargetPlatform.windows:
config.onDidGainAccessibilityFocus = onDidGainAccessibilityFocus;
break;
}
config.textDirection = textDirection; config.textDirection = textDirection;
if (isInteractive) { if (isInteractive) {
config.onIncrease = increaseAction; config.onIncrease = increaseAction;
......
...@@ -15,7 +15,6 @@ import 'package:flutter/src/physics/utils.dart' show nearEqual; ...@@ -15,7 +15,6 @@ import 'package:flutter/src/physics/utils.dart' show nearEqual;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart'; import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
// A thumb shape that also logs its repaint center. // A thumb shape that also logs its repaint center.
class LoggingThumbShape extends SliderComponentShape { class LoggingThumbShape extends SliderComponentShape {
...@@ -1148,8 +1147,6 @@ void main() { ...@@ -1148,8 +1147,6 @@ void main() {
}); });
testWidgets('Slider Semantics', (WidgetTester tester) async { testWidgets('Slider Semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Directionality( home: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1164,49 +1161,24 @@ void main() { ...@@ -1164,49 +1161,24 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
SemanticsNode semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( children: <Matcher>[
children: <TestSemantics>[ matchesSemantics(
TestSemantics( isEnabled: true,
id: 1, isSlider: true,
textDirection: TextDirection.ltr, isFocusable: true,
children: <TestSemantics>[ hasEnabledState: true,
TestSemantics( hasIncreaseAction: true,
id: 2, hasDecreaseAction: true,
children: <TestSemantics>[ value: '50%',
TestSemantics( increasedValue: '55%',
id: 3, decreasedValue: '45%',
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], textDirection: TextDirection.ltr,
children: <TestSemantics>[ ),
TestSemantics( ],
id: 4,
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.increase,
SemanticsAction.decrease,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
), ),
); );
...@@ -1223,95 +1195,27 @@ void main() { ...@@ -1223,95 +1195,27 @@ void main() {
), ),
)); ));
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,
// isFocusable is delayed by 1 frame.
SemanticsFlag.isFocusable,
SemanticsFlag.isSlider,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
),
);
await tester.pump(); await tester.pump();
semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( scopesRoute: true,
children: <TestSemantics>[ children: <Matcher>[
TestSemantics( matchesSemantics(
id: 1, isSlider: true,
textDirection: TextDirection.ltr, hasEnabledState: true,
children: <TestSemantics>[ value: '50%',
TestSemantics( increasedValue: '55%',
id: 2, decreasedValue: '45%',
children: <TestSemantics>[ textDirection: TextDirection.ltr,
TestSemantics( ),
id: 3, ],
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
TestSemantics(
id: 4,
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isSlider,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
), ),
); );
semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux }));
testWidgets('Slider Semantics', (WidgetTester tester) async { testWidgets('Slider Semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Theme( home: Theme(
...@@ -1330,41 +1234,26 @@ void main() { ...@@ -1330,41 +1234,26 @@ void main() {
), ),
); );
await tester.pumpAndSettle();
SemanticsNode semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( children: <Matcher>[
children: <TestSemantics>[ matchesSemantics(
TestSemantics( isEnabled: true,
id: 1, isSlider: true,
textDirection: TextDirection.ltr, isFocusable: true,
children: <TestSemantics>[ hasEnabledState: true,
TestSemantics( hasIncreaseAction: true,
id: 2, hasDecreaseAction: true,
children: <TestSemantics>[ value: '50%',
TestSemantics( increasedValue: '60%',
id: 3, decreasedValue: '40%',
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], textDirection: TextDirection.ltr,
children: <TestSemantics>[ ),
TestSemantics( ],
id: 4,
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isSlider],
actions: <SemanticsAction>[SemanticsAction.increase, SemanticsAction.decrease],
value: '50%',
increasedValue: '60%',
decreasedValue: '40%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
), ),
); );
...@@ -1381,48 +1270,26 @@ void main() { ...@@ -1381,48 +1270,26 @@ void main() {
), ),
)); ));
semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( scopesRoute: true,
children: <TestSemantics>[ children: <Matcher>[
TestSemantics( matchesSemantics(
id: 1, isSlider: true,
textDirection: TextDirection.ltr, hasEnabledState: true,
children: <TestSemantics>[ value: '50%',
TestSemantics( increasedValue: '60%',
id: 2, decreasedValue: '40%',
children: <TestSemantics>[ textDirection: TextDirection.ltr,
TestSemantics( ),
id: 3, ],
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
TestSemantics(
id: 5,
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState, SemanticsFlag.isSlider],
value: '50%',
increasedValue: '60%',
decreasedValue: '40%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
), ),
); );
semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('Slider Semantics', (WidgetTester tester) async { testWidgets('Slider Semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Directionality( home: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1437,50 +1304,25 @@ void main() { ...@@ -1437,50 +1304,25 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
SemanticsNode semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( children: <Matcher>[
children: <TestSemantics>[ matchesSemantics(
TestSemantics( isEnabled: true,
id: 1, isSlider: true,
textDirection: TextDirection.ltr, isFocusable: true,
children: <TestSemantics>[ hasEnabledState: true,
TestSemantics( hasIncreaseAction: true,
id: 2, hasDecreaseAction: true,
children: <TestSemantics>[ hasDidGainAccessibilityFocusAction: true,
TestSemantics( value: '50%',
id: 3, increasedValue: '55%',
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], decreasedValue: '45%',
children: <TestSemantics>[ textDirection: TextDirection.ltr,
TestSemantics( ),
id: 4, ],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.increase,
SemanticsAction.decrease,
SemanticsAction.didGainAccessibilityFocus,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
), ),
); );
...@@ -1497,101 +1339,28 @@ void main() { ...@@ -1497,101 +1339,28 @@ void main() {
), ),
)); ));
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,
// isFocusable is delayed by 1 frame.
SemanticsFlag.isFocusable,
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.didGainAccessibilityFocus,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
),
);
await tester.pump(); await tester.pump();
semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( scopesRoute: true,
children: <TestSemantics>[ children: <Matcher>[
TestSemantics( matchesSemantics(
id: 1, isSlider: true,
textDirection: TextDirection.ltr, hasEnabledState: true,
children: <TestSemantics>[ hasDidGainAccessibilityFocusAction: true,
TestSemantics( value: '50%',
id: 2, increasedValue: '55%',
children: <TestSemantics>[ decreasedValue: '45%',
TestSemantics( textDirection: TextDirection.ltr,
id: 3, ),
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], ],
children: <TestSemantics>[
TestSemantics(
id: 4,
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isSlider,
],
actions: <SemanticsAction>[
SemanticsAction.didGainAccessibilityFocus,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
), ),
); );
semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.windows })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.windows }));
testWidgets('Slider semantics with custom formatter', (WidgetTester tester) async { testWidgets('Slider semantics with custom formatter', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Directionality( home: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1607,50 +1376,32 @@ void main() { ...@@ -1607,50 +1376,32 @@ void main() {
), ),
)); ));
await tester.pumpAndSettle();
final SemanticsNode semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( children: <Matcher>[
children: <TestSemantics>[ matchesSemantics(
TestSemantics( isEnabled: true,
id: 1, isSlider: true,
textDirection: TextDirection.ltr, isFocusable: true,
children: <TestSemantics>[ hasEnabledState: true,
TestSemantics( hasIncreaseAction: true,
id: 2, hasDecreaseAction: true,
children: <TestSemantics>[ value: '40',
TestSemantics( increasedValue: '60',
id: 3, decreasedValue: '20',
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], textDirection: TextDirection.ltr,
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();
}); });
// Regression test for https://github.com/flutter/flutter/issues/101868 // Regression test for https://github.com/flutter/flutter/issues/101868
testWidgets('Slider.label info should not write to semantic node', (WidgetTester tester) async { testWidgets('Slider.label info should not write to semantic node', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Directionality( home: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1667,44 +1418,28 @@ void main() { ...@@ -1667,44 +1418,28 @@ void main() {
), ),
)); ));
await tester.pumpAndSettle();
final SemanticsNode semanticsNode = tester.getSemantics(find.byType(Slider));
expect( expect(
semantics, semanticsNode,
hasSemantics( matchesSemantics(
TestSemantics.root( children: <Matcher>[
children: <TestSemantics>[ matchesSemantics(
TestSemantics( isEnabled: true,
id: 1, isSlider: true,
textDirection: TextDirection.ltr, isFocusable: true,
children: <TestSemantics>[ hasEnabledState: true,
TestSemantics( hasIncreaseAction: true,
id: 2, hasDecreaseAction: true,
children: <TestSemantics>[ value: '40',
TestSemantics( increasedValue: '60',
id: 3, decreasedValue: '20',
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], textDirection: TextDirection.ltr,
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 {
...@@ -2376,7 +2111,6 @@ void main() { ...@@ -2376,7 +2111,6 @@ void main() {
}); });
testWidgets('Slider gains keyboard focus when it gains semantics focus on Windows', (WidgetTester tester) async { testWidgets('Slider gains keyboard focus when it gains semantics focus on Windows', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!; final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!;
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2391,55 +2125,32 @@ void main() { ...@@ -2391,55 +2125,32 @@ void main() {
), ),
); );
expect(semantics, hasSemantics( final SemanticsNode semanticsNode = tester.getSemantics(find.byType(Slider));
TestSemantics.root( expect(
children: <TestSemantics>[ semanticsNode,
TestSemantics( matchesSemantics(
id: 1, children: <Matcher>[
matchesSemantics(
isEnabled: true,
isSlider: true,
isFocusable: true,
hasEnabledState: true,
hasIncreaseAction: true,
hasDecreaseAction: true,
hasDidGainAccessibilityFocusAction: true,
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr, 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,
SemanticsAction.didGainAccessibilityFocus,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
), ),
], ],
), ),
ignoreRect: true, );
ignoreTransform: true,
));
expect(focusNode.hasFocus, isFalse); expect(focusNode.hasFocus, isFalse);
semanticsOwner.performAction(4, SemanticsAction.didGainAccessibilityFocus); semanticsOwner.performAction(5, SemanticsAction.didGainAccessibilityFocus);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(focusNode.hasFocus, isTrue); expect(focusNode.hasFocus, isTrue);
semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.windows })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.windows }));
testWidgets('Value indicator appears when it should', (WidgetTester tester) async { testWidgets('Value indicator appears when it should', (WidgetTester tester) async {
......
...@@ -290,7 +290,7 @@ void main() { ...@@ -290,7 +290,7 @@ void main() {
}); });
testWidgets('SemanticsDebugger slider', (WidgetTester tester) async { testWidgets('SemanticsDebugger slider', (WidgetTester tester) async {
double value = 0.75; double value = 0.50;
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -322,7 +322,7 @@ void main() { ...@@ -322,7 +322,7 @@ void main() {
// interpreted as a gesture by the semantics debugger and sent to the widget // interpreted as a gesture by the semantics debugger and sent to the widget
// as a semantic action that always moves by 10% of the complete track. // as a semantic action that always moves by 10% of the complete track.
await tester.fling(find.byType(Slider), const Offset(-100.0, 0.0), 2000.0, warnIfMissed: false); // hitting the debugger await tester.fling(find.byType(Slider), const Offset(-100.0, 0.0), 2000.0, warnIfMissed: false); // hitting the debugger
expect(value, equals(0.70)); expect(value, equals(0.45));
}); });
testWidgets('SemanticsDebugger checkbox', (WidgetTester tester) async { testWidgets('SemanticsDebugger checkbox', (WidgetTester tester) async {
......
...@@ -865,7 +865,7 @@ void main() { ...@@ -865,7 +865,7 @@ void main() {
await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget())); await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget()));
// We're expecting the traversal to start where the slider is. // We're expecting the traversal to start where the slider is.
final List<Matcher> expectedMatchers = <Matcher>[...fullTraversalMatchers]..removeRange(0, 8); final List<Matcher> expectedMatchers = <Matcher>[...fullTraversalMatchers]..removeRange(0, 7);
expect( expect(
tester.semantics.simulatedAccessibilityTraversal(start: find.byType(Slider)), tester.semantics.simulatedAccessibilityTraversal(start: find.byType(Slider)),
...@@ -887,7 +887,7 @@ void main() { ...@@ -887,7 +887,7 @@ void main() {
await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget())); await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget()));
// We're expecting the traversal to end where the slider is, inclusive. // We're expecting the traversal to end where the slider is, inclusive.
final Iterable<Matcher> expectedMatchers = <Matcher>[...fullTraversalMatchers].getRange(0, 9); final Iterable<Matcher> expectedMatchers = <Matcher>[...fullTraversalMatchers].getRange(0, 8);
expect( expect(
tester.semantics.simulatedAccessibilityTraversal(end: find.byType(Slider)), tester.semantics.simulatedAccessibilityTraversal(end: find.byType(Slider)),
...@@ -909,7 +909,7 @@ void main() { ...@@ -909,7 +909,7 @@ void main() {
await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget())); await tester.pumpWidget(const MaterialApp(home: _SemanticsTestWidget()));
// We're expecting the traversal to start at the text field and end at the slider. // We're expecting the traversal to start at the text field and end at the slider.
final Iterable<Matcher> expectedMatchers = <Matcher>[...fullTraversalMatchers].getRange(1, 9); final Iterable<Matcher> expectedMatchers = <Matcher>[...fullTraversalMatchers].getRange(1, 8);
expect( expect(
tester.semantics.simulatedAccessibilityTraversal( tester.semantics.simulatedAccessibilityTraversal(
......
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