Skip to content

Commit 8473da2

Browse files
authored
Fix Slider semantic node size (#115285)
1 parent 333397a commit 8473da2

File tree

4 files changed

+233
-504
lines changed

4 files changed

+233
-504
lines changed

packages/flutter/lib/src/material/slider.dart

+75-57
Original file line numberDiff line numberDiff line change
@@ -821,22 +821,11 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
821821
// in range_slider.dart.
822822
Size screenSize() => MediaQuery.of(context).size;
823823

824-
VoidCallback? handleDidGainAccessibilityFocus;
825-
switch (theme.platform) {
826-
case TargetPlatform.android:
827-
case TargetPlatform.fuchsia:
828-
case TargetPlatform.iOS:
829-
case TargetPlatform.linux:
830-
case TargetPlatform.macOS:
831-
break;
832-
case TargetPlatform.windows:
833-
handleDidGainAccessibilityFocus = () {
834-
// Automatically activate the slider when it receives a11y focus.
835-
if (!focusNode.hasFocus && focusNode.canRequestFocus) {
836-
focusNode.requestFocus();
837-
}
838-
};
839-
break;
824+
void handleDidGainAccessibilityFocus() {
825+
// Automatically activate the slider when it receives a11y focus.
826+
if (!focusNode.hasFocus && focusNode.canRequestFocus) {
827+
focusNode.requestFocus();
828+
}
840829
}
841830

842831
final Map<ShortcutActivator, Intent> shortcutMap;
@@ -857,38 +846,35 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
857846
? math.min(MediaQuery.of(context).textScaleFactor, 1.3)
858847
: MediaQuery.of(context).textScaleFactor;
859848

860-
return Semantics(
861-
container: true,
862-
slider: true,
863-
onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
864-
child: FocusableActionDetector(
865-
actions: _actionMap,
866-
shortcuts: shortcutMap,
867-
focusNode: focusNode,
868-
autofocus: widget.autofocus,
869-
enabled: _enabled,
870-
onShowFocusHighlight: _handleFocusHighlightChanged,
871-
onShowHoverHighlight: _handleHoverChanged,
872-
mouseCursor: effectiveMouseCursor,
873-
child: CompositedTransformTarget(
874-
link: _layerLink,
875-
child: _SliderRenderObjectWidget(
876-
key: _renderObjectKey,
877-
value: _convert(widget.value),
878-
secondaryTrackValue: (widget.secondaryTrackValue != null) ? _convert(widget.secondaryTrackValue!) : null,
879-
divisions: widget.divisions,
880-
label: widget.label,
881-
sliderTheme: sliderTheme,
882-
textScaleFactor: textScaleFactor,
883-
screenSize: screenSize(),
884-
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
885-
onChangeStart: _handleDragStart,
886-
onChangeEnd: _handleDragEnd,
887-
state: this,
888-
semanticFormatterCallback: widget.semanticFormatterCallback,
889-
hasFocus: _focused,
890-
hovering: _hovering,
891-
),
849+
return FocusableActionDetector(
850+
actions: _actionMap,
851+
shortcuts: shortcutMap,
852+
focusNode: focusNode,
853+
autofocus: widget.autofocus,
854+
enabled: _enabled,
855+
onShowFocusHighlight: _handleFocusHighlightChanged,
856+
onShowHoverHighlight: _handleHoverChanged,
857+
mouseCursor: effectiveMouseCursor,
858+
includeFocusSemantics: false,
859+
child: CompositedTransformTarget(
860+
link: _layerLink,
861+
child: _SliderRenderObjectWidget(
862+
key: _renderObjectKey,
863+
value: _convert(widget.value),
864+
secondaryTrackValue: (widget.secondaryTrackValue != null) ? _convert(widget.secondaryTrackValue!) : null,
865+
divisions: widget.divisions,
866+
label: widget.label,
867+
sliderTheme: sliderTheme,
868+
textScaleFactor: textScaleFactor,
869+
screenSize: screenSize(),
870+
onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null,
871+
onChangeStart: _handleDragStart,
872+
onChangeEnd: _handleDragEnd,
873+
state: this,
874+
semanticFormatterCallback: widget.semanticFormatterCallback,
875+
onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
876+
hasFocus: _focused,
877+
hovering: _hovering,
892878
),
893879
),
894880
);
@@ -949,6 +935,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
949935
required this.onChangeEnd,
950936
required this.state,
951937
required this.semanticFormatterCallback,
938+
required this.onDidGainAccessibilityFocus,
952939
required this.hasFocus,
953940
required this.hovering,
954941
});
@@ -964,6 +951,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
964951
final ValueChanged<double>? onChangeStart;
965952
final ValueChanged<double>? onChangeEnd;
966953
final SemanticFormatterCallback? semanticFormatterCallback;
954+
final VoidCallback? onDidGainAccessibilityFocus;
967955
final _SliderState state;
968956
final bool hasFocus;
969957
final bool hovering;
@@ -984,6 +972,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
984972
state: state,
985973
textDirection: Directionality.of(context),
986974
semanticFormatterCallback: semanticFormatterCallback,
975+
onDidGainAccessibilityFocus: onDidGainAccessibilityFocus,
987976
platform: Theme.of(context).platform,
988977
hasFocus: hasFocus,
989978
hovering: hovering,
@@ -1008,6 +997,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
1008997
..onChangeEnd = onChangeEnd
1009998
..textDirection = Directionality.of(context)
1010999
..semanticFormatterCallback = semanticFormatterCallback
1000+
..onDidGainAccessibilityFocus = onDidGainAccessibilityFocus
10111001
..platform = Theme.of(context).platform
10121002
..hasFocus = hasFocus
10131003
..hovering = hovering
@@ -1029,6 +1019,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
10291019
required TargetPlatform platform,
10301020
required ValueChanged<double>? onChanged,
10311021
required SemanticFormatterCallback? semanticFormatterCallback,
1022+
required this.onDidGainAccessibilityFocus,
10321023
required this.onChangeStart,
10331024
required this.onChangeEnd,
10341025
required _SliderState state,
@@ -1114,6 +1105,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
11141105
bool _active = false;
11151106
double _currentDragValue = 0.0;
11161107
Rect? overlayRect;
1108+
late Offset _thumbCenter;
11171109

11181110
// This rect is used in gesture calculations, where the gesture coordinates
11191111
// are relative to the sliders origin. Therefore, the offset is passed as
@@ -1259,6 +1251,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
12591251
}
12601252
}
12611253

1254+
VoidCallback? onDidGainAccessibilityFocus;
12621255
ValueChanged<double>? onChangeStart;
12631256
ValueChanged<double>? onChangeEnd;
12641257

@@ -1582,10 +1575,10 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
15821575
sliderTheme: _sliderTheme,
15831576
isDiscrete: isDiscrete,
15841577
);
1585-
final Offset thumbCenter = Offset(trackRect.left + visualPosition * trackRect.width, trackRect.center.dy);
1578+
_thumbCenter = Offset(trackRect.left + visualPosition * trackRect.width, trackRect.center.dy);
15861579
if (isInteractive) {
15871580
final Size overlaySize = sliderTheme.overlayShape!.getPreferredSize(isInteractive, false);
1588-
overlayRect = Rect.fromCircle(center: thumbCenter, radius: overlaySize.width / 2.0);
1581+
overlayRect = Rect.fromCircle(center: _thumbCenter, radius: overlaySize.width / 2.0);
15891582
}
15901583
final Offset? secondaryOffset = (secondaryVisualPosition != null) ? Offset(trackRect.left + secondaryVisualPosition * trackRect.width, trackRect.center.dy) : null;
15911584

@@ -1596,7 +1589,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
15961589
sliderTheme: _sliderTheme,
15971590
enableAnimation: _enableAnimation,
15981591
textDirection: _textDirection,
1599-
thumbCenter: thumbCenter,
1592+
thumbCenter: _thumbCenter,
16001593
secondaryOffset: secondaryOffset,
16011594
isDiscrete: isDiscrete,
16021595
isEnabled: isInteractive,
@@ -1605,7 +1598,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
16051598
if (!_overlayAnimation.isDismissed) {
16061599
_sliderTheme.overlayShape!.paint(
16071600
context,
1608-
thumbCenter,
1601+
_thumbCenter,
16091602
activationAnimation: _overlayAnimation,
16101603
enableAnimation: _enableAnimation,
16111604
isDiscrete: isDiscrete,
@@ -1642,7 +1635,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
16421635
sliderTheme: _sliderTheme,
16431636
enableAnimation: _enableAnimation,
16441637
textDirection: _textDirection,
1645-
thumbCenter: thumbCenter,
1638+
thumbCenter: _thumbCenter,
16461639
isEnabled: isInteractive,
16471640
);
16481641
}
@@ -1655,7 +1648,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
16551648
if (attached) {
16561649
_sliderTheme.valueIndicatorShape!.paint(
16571650
context,
1658-
offset + thumbCenter,
1651+
offset + _thumbCenter,
16591652
activationAnimation: _valueIndicatorAnimation,
16601653
enableAnimation: _enableAnimation,
16611654
isDiscrete: isDiscrete,
@@ -1674,7 +1667,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
16741667

16751668
_sliderTheme.thumbShape!.paint(
16761669
context,
1677-
thumbCenter,
1670+
_thumbCenter,
16781671
activationAnimation: _overlayAnimation,
16791672
enableAnimation: _enableAnimation,
16801673
isDiscrete: isDiscrete,
@@ -1688,22 +1681,47 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
16881681
);
16891682
}
16901683

1684+
@override
1685+
void assembleSemanticsNode(SemanticsNode node, SemanticsConfiguration config, Iterable<SemanticsNode> children) {
1686+
node.rect = Rect.fromCenter(
1687+
center: _thumbCenter,
1688+
width: kMinInteractiveDimension,
1689+
height: kMinInteractiveDimension,
1690+
);
1691+
1692+
node.updateWith(config: config);
1693+
}
1694+
16911695
@override
16921696
void describeSemanticsConfiguration(SemanticsConfiguration config) {
16931697
super.describeSemanticsConfiguration(config);
16941698

16951699
// The Slider widget has its own Focus widget with semantics information,
1696-
// and we want that semantics node to collect the semantics information here
1700+
// and want that semantics node to collect the semantics information here
16971701
// so that it's all in the same node: otherwise Talkback sees that the node
16981702
// has focusable children, and it won't focus the Slider's Focus widget
16991703
// because it thinks the Focus widget's node doesn't have anything to say
17001704
// (which it doesn't, but this child does). Aggregating the semantic
17011705
// information into one node means that Talkback will recognize that it has
17021706
// something to say and focus it when it receives keyboard focus.
17031707
// (See https://github.com/flutter/flutter/issues/57038 for context).
1704-
config.isSemanticBoundary = false;
1708+
config.isSemanticBoundary = true;
17051709

17061710
config.isEnabled = isInteractive;
1711+
config.isSlider = true;
1712+
config.isFocusable = isInteractive;
1713+
config.isFocused = hasFocus;
1714+
switch (_platform) {
1715+
case TargetPlatform.android:
1716+
case TargetPlatform.fuchsia:
1717+
case TargetPlatform.iOS:
1718+
case TargetPlatform.linux:
1719+
case TargetPlatform.macOS:
1720+
break;
1721+
case TargetPlatform.windows:
1722+
config.onDidGainAccessibilityFocus = onDidGainAccessibilityFocus;
1723+
break;
1724+
}
17071725
config.textDirection = textDirection;
17081726
if (isInteractive) {
17091727
config.onIncrease = increaseAction;

0 commit comments

Comments
 (0)