Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 1f891a0

Browse files
authored
Fix RangeSlider semantics node size (#114999)
* Fix `RangeSlider` semantics node size and add RTL semantics test * Add TODO comments
1 parent 09a4f23 commit 1f891a0

File tree

2 files changed

+143
-22
lines changed

2 files changed

+143
-22
lines changed

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

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,8 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
840840
late TapGestureRecognizer _tap;
841841
bool _active = false;
842842
late RangeValues _newValues;
843+
late Offset _startThumbCenter;
844+
late Offset _endThumbCenter;
843845

844846
bool get isEnabled => onChanged != null;
845847

@@ -1303,8 +1305,8 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
13031305
sliderTheme: _sliderTheme,
13041306
isDiscrete: isDiscrete,
13051307
);
1306-
final Offset startThumbCenter = Offset(trackRect.left + startVisualPosition * trackRect.width, trackRect.center.dy);
1307-
final Offset endThumbCenter = Offset(trackRect.left + endVisualPosition * trackRect.width, trackRect.center.dy);
1308+
_startThumbCenter = Offset(trackRect.left + startVisualPosition * trackRect.width, trackRect.center.dy);
1309+
_endThumbCenter = Offset(trackRect.left + endVisualPosition * trackRect.width, trackRect.center.dy);
13081310

13091311
_sliderTheme.rangeTrackShape!.paint(
13101312
context,
@@ -1313,8 +1315,8 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
13131315
sliderTheme: _sliderTheme,
13141316
enableAnimation: _enableAnimation,
13151317
textDirection: _textDirection,
1316-
startThumbCenter: startThumbCenter,
1317-
endThumbCenter: endThumbCenter,
1318+
startThumbCenter: _startThumbCenter,
1319+
endThumbCenter: _endThumbCenter,
13181320
isDiscrete: isDiscrete,
13191321
isEnabled: isEnabled,
13201322
);
@@ -1327,7 +1329,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
13271329
if (startThumbSelected) {
13281330
_sliderTheme.overlayShape!.paint(
13291331
context,
1330-
startThumbCenter,
1332+
_startThumbCenter,
13311333
activationAnimation: _overlayAnimation,
13321334
enableAnimation: _enableAnimation,
13331335
isDiscrete: isDiscrete,
@@ -1343,7 +1345,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
13431345
if (endThumbSelected) {
13441346
_sliderTheme.overlayShape!.paint(
13451347
context,
1346-
endThumbCenter,
1348+
_endThumbCenter,
13471349
activationAnimation: _overlayAnimation,
13481350
enableAnimation: _enableAnimation,
13491351
isDiscrete: isDiscrete,
@@ -1381,21 +1383,21 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
13811383
sliderTheme: _sliderTheme,
13821384
enableAnimation: _enableAnimation,
13831385
textDirection: _textDirection,
1384-
startThumbCenter: startThumbCenter,
1385-
endThumbCenter: endThumbCenter,
1386+
startThumbCenter: _startThumbCenter,
1387+
endThumbCenter: _endThumbCenter,
13861388
isEnabled: isEnabled,
13871389
);
13881390
}
13891391
}
13901392
}
13911393

1392-
final double thumbDelta = (endThumbCenter.dx - startThumbCenter.dx).abs();
1394+
final double thumbDelta = (_endThumbCenter.dx - _startThumbCenter.dx).abs();
13931395

13941396
final bool isLastThumbStart = _lastThumbSelection == Thumb.start;
13951397
final Thumb bottomThumb = isLastThumbStart ? Thumb.end : Thumb.start;
13961398
final Thumb topThumb = isLastThumbStart ? Thumb.start : Thumb.end;
1397-
final Offset bottomThumbCenter = isLastThumbStart ? endThumbCenter : startThumbCenter;
1398-
final Offset topThumbCenter = isLastThumbStart ? startThumbCenter : endThumbCenter;
1399+
final Offset bottomThumbCenter = isLastThumbStart ? _endThumbCenter : _startThumbCenter;
1400+
final Offset topThumbCenter = isLastThumbStart ? _startThumbCenter : _endThumbCenter;
13991401
final TextPainter bottomLabelPainter = isLastThumbStart ? _endLabelPainter : _startLabelPainter;
14001402
final TextPainter topLabelPainter = isLastThumbStart ? _startLabelPainter : _endLabelPainter;
14011403
final double bottomValue = isLastThumbStart ? endValue : startValue;
@@ -1441,15 +1443,15 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
14411443
if (shouldPaintValueIndicators) {
14421444
final double startOffset = sliderTheme.rangeValueIndicatorShape!.getHorizontalShift(
14431445
parentBox: this,
1444-
center: startThumbCenter,
1446+
center: _startThumbCenter,
14451447
labelPainter: _startLabelPainter,
14461448
activationAnimation: _valueIndicatorAnimation,
14471449
textScaleFactor: textScaleFactor,
14481450
sizeWithOverflow: resolvedscreenSize,
14491451
);
14501452
final double endOffset = sliderTheme.rangeValueIndicatorShape!.getHorizontalShift(
14511453
parentBox: this,
1452-
center: endThumbCenter,
1454+
center: _endThumbCenter,
14531455
labelPainter: _endLabelPainter,
14541456
activationAnimation: _valueIndicatorAnimation,
14551457
textScaleFactor: textScaleFactor,
@@ -1575,8 +1577,16 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
15751577
);
15761578

15771579
// Split the semantics node area between the start and end nodes.
1578-
final Rect leftRect = Rect.fromPoints(node.rect.topLeft, node.rect.bottomCenter);
1579-
final Rect rightRect = Rect.fromPoints(node.rect.topCenter, node.rect.bottomRight);
1580+
final Rect leftRect = Rect.fromCenter(
1581+
center: _startThumbCenter,
1582+
width: kMinInteractiveDimension,
1583+
height: kMinInteractiveDimension,
1584+
);
1585+
final Rect rightRect = Rect.fromCenter(
1586+
center: _endThumbCenter,
1587+
width: kMinInteractiveDimension,
1588+
height: kMinInteractiveDimension,
1589+
);
15801590
switch (textDirection) {
15811591
case TextDirection.ltr:
15821592
_startSemanticsNode!.rect = leftRect;

packages/flutter/test/material/range_slider_test.dart

Lines changed: 118 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,7 +1853,7 @@ void main() {
18531853
);
18541854
});
18551855

1856-
testWidgets('Range Slider Semantics', (WidgetTester tester) async {
1856+
testWidgets('Range Slider Semantics - ltr', (WidgetTester tester) async {
18571857
await tester.pumpWidget(
18581858
MaterialApp(
18591859
home: Theme(
@@ -1862,7 +1862,7 @@ void main() {
18621862
textDirection: TextDirection.ltr,
18631863
child: Material(
18641864
child: RangeSlider(
1865-
values: const RangeValues(10.0, 12.0),
1865+
values: const RangeValues(10.0, 30.0),
18661866
max: 100.0,
18671867
onChanged: (RangeValues v) { },
18681868
),
@@ -1874,8 +1874,9 @@ void main() {
18741874

18751875
await tester.pumpAndSettle();
18761876

1877+
final SemanticsNode semanticsNode = tester.getSemantics(find.byType(RangeSlider));
18771878
expect(
1878-
tester.getSemantics(find.byType(RangeSlider)),
1879+
semanticsNode,
18791880
matchesSemantics(
18801881
scopesRoute: true,
18811882
children:<Matcher>[
@@ -1888,24 +1889,134 @@ void main() {
18881889
hasIncreaseAction: true,
18891890
hasDecreaseAction: true,
18901891
value: '10%',
1891-
increasedValue: '10%',
1892+
increasedValue: '15%',
18921893
decreasedValue: '5%',
1894+
rect: const Rect.fromLTRB(75.2, 276.0, 123.2, 324.0),
18931895
),
18941896
matchesSemantics(
18951897
isEnabled: true,
18961898
isSlider: true,
18971899
hasEnabledState: true,
18981900
hasIncreaseAction: true,
18991901
hasDecreaseAction: true,
1900-
value: '12%',
1901-
increasedValue: '17%',
1902-
decreasedValue: '12%',
1902+
value: '30%',
1903+
increasedValue: '35%',
1904+
decreasedValue: '25%',
1905+
rect: const Rect.fromLTRB(225.6, 276.0, 273.6, 324.0),
19031906
),
19041907
],
19051908
),
19061909
],
19071910
),
19081911
);
1912+
1913+
// TODO(tahatesser): This is a workaround for matching
1914+
// the semantics node rects by avoiding floating point errors.
1915+
// https://github.com/flutter/flutter/issues/115079
1916+
// Get semantics node rects.
1917+
final List<Rect> rects = <Rect>[];
1918+
semanticsNode.visitChildren((SemanticsNode node) {
1919+
node.visitChildren((SemanticsNode node) {
1920+
// Round rect values to avoid floating point errors.
1921+
rects.add(
1922+
Rect.fromLTRB(
1923+
node.rect.left.roundToDouble(),
1924+
node.rect.top.roundToDouble(),
1925+
node.rect.right.roundToDouble(),
1926+
node.rect.bottom.roundToDouble(),
1927+
),
1928+
);
1929+
return true;
1930+
});
1931+
return true;
1932+
});
1933+
// Test that the semantics node rect sizes are correct.
1934+
expect(rects, <Rect>[
1935+
const Rect.fromLTRB(75.0, 276.0, 123.0, 324.0),
1936+
const Rect.fromLTRB(226.0, 276.0, 274.0, 324.0)
1937+
]);
1938+
});
1939+
1940+
testWidgets('Range Slider Semantics - rtl', (WidgetTester tester) async {
1941+
await tester.pumpWidget(
1942+
MaterialApp(
1943+
home: Theme(
1944+
data: ThemeData.light(),
1945+
child: Directionality(
1946+
textDirection: TextDirection.rtl,
1947+
child: Material(
1948+
child: RangeSlider(
1949+
values: const RangeValues(10.0, 30.0),
1950+
max: 100.0,
1951+
onChanged: (RangeValues v) { },
1952+
),
1953+
),
1954+
),
1955+
),
1956+
),
1957+
);
1958+
1959+
await tester.pumpAndSettle();
1960+
1961+
final SemanticsNode semanticsNode = tester.getSemantics(find.byType(RangeSlider));
1962+
expect(
1963+
semanticsNode,
1964+
matchesSemantics(
1965+
scopesRoute: true,
1966+
children:<Matcher>[
1967+
matchesSemantics(
1968+
children: <Matcher>[
1969+
matchesSemantics(
1970+
isEnabled: true,
1971+
isSlider: true,
1972+
hasEnabledState: true,
1973+
hasIncreaseAction: true,
1974+
hasDecreaseAction: true,
1975+
value: '10%',
1976+
increasedValue: '15%',
1977+
decreasedValue: '5%',
1978+
),
1979+
matchesSemantics(
1980+
isEnabled: true,
1981+
isSlider: true,
1982+
hasEnabledState: true,
1983+
hasIncreaseAction: true,
1984+
hasDecreaseAction: true,
1985+
value: '30%',
1986+
increasedValue: '35%',
1987+
decreasedValue: '25%',
1988+
),
1989+
],
1990+
),
1991+
],
1992+
),
1993+
);
1994+
1995+
// TODO(tahatesser): This is a workaround for matching
1996+
// the semantics node rects by avoiding floating point errors.
1997+
// https://github.com/flutter/flutter/issues/115079
1998+
// Get semantics node rects.
1999+
final List<Rect> rects = <Rect>[];
2000+
semanticsNode.visitChildren((SemanticsNode node) {
2001+
node.visitChildren((SemanticsNode node) {
2002+
// Round rect values to avoid floating point errors.
2003+
rects.add(
2004+
Rect.fromLTRB(
2005+
node.rect.left.roundToDouble(),
2006+
node.rect.top.roundToDouble(),
2007+
node.rect.right.roundToDouble(),
2008+
node.rect.bottom.roundToDouble(),
2009+
),
2010+
);
2011+
return true;
2012+
});
2013+
return true;
2014+
});
2015+
// Test that the semantics node rect sizes are correct.
2016+
expect(rects, <Rect>[
2017+
const Rect.fromLTRB(526.0, 276.0, 574.0, 324.0),
2018+
const Rect.fromLTRB(677.0, 276.0, 725.0, 324.0)
2019+
]);
19092020
});
19102021

19112022
testWidgets('Range Slider implements debugFillProperties', (WidgetTester tester) async {

0 commit comments

Comments
 (0)