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