Fix: Range slider show overlay for both thumbs on hovering one (#165393)

Fix: Range slider show overlay for both thumbs on hovering one
fixes: #165281 

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
This commit is contained in:
Kishan Rathore
2025-04-03 06:03:58 +05:30
committed by GitHub
parent e1213e4869
commit 503672ff1d
2 changed files with 138 additions and 2 deletions

View File

@@ -1517,8 +1517,8 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
isEnabled: isEnabled,
);
final bool startThumbSelected = _lastThumbSelection == Thumb.start;
final bool endThumbSelected = _lastThumbSelection == Thumb.end;
final bool startThumbSelected = _lastThumbSelection == Thumb.start && !hoveringEndThumb;
final bool endThumbSelected = _lastThumbSelection == Thumb.end && !hoveringStartThumb;
final Size resolvedscreenSize = screenSize.isEmpty ? size : screenSize;
if (!_overlayAnimation.isDismissed) {

View File

@@ -2810,4 +2810,140 @@ void main() {
semantics.dispose();
}, semanticsEnabled: false);
testWidgets('RangeSlider overlay appears correctly for specific thumb interactions', (
WidgetTester tester,
) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
RangeValues values = const RangeValues(50, 70);
const Color hoverColor = Color(0xffff0000);
const Color dragColor = Color(0xff0000ff);
Widget buildApp() {
return MaterialApp(
home: Directionality(
textDirection: TextDirection.ltr,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: RangeSlider(
values: values,
max: 100.0,
overlayColor: WidgetStateProperty.resolveWith<Color?>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.hovered)) {
return hoverColor;
}
if (states.contains(WidgetState.dragged)) {
return dragColor;
}
return null;
}),
onChanged: (RangeValues newValues) {
setState(() {
values = newValues;
});
},
onChangeStart: (RangeValues newValues) {},
onChangeEnd: (RangeValues newValues) {},
),
),
);
},
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
// Initial state - no overlay.
expect(
Material.of(tester.element(find.byType(RangeSlider))),
isNot(paints..circle(color: dragColor)),
);
// Drag start thumb to left.
final Offset topThumbLocation = tester.getCenter(find.byType(RangeSlider));
final TestGesture dragStartThumb = await tester.startGesture(topThumbLocation);
await tester.pump(kPressTimeout);
await dragStartThumb.moveBy(const Offset(-20.0, 0));
await tester.pumpAndSettle();
// Verify overlay is visible and shadow is visible on single thumb.
expect(
Material.of(tester.element(find.byType(RangeSlider))),
paints
..circle(color: dragColor)
..path(color: Colors.black, style: PaintingStyle.stroke, strokeWidth: 2.0)
..path(color: Colors.black, style: PaintingStyle.stroke, strokeWidth: 12.0),
);
// Move back and release.
await dragStartThumb.moveBy(const Offset(20.0, 0));
await dragStartThumb.up();
await tester.pumpAndSettle();
// Verify overlay and shadow disappears
expect(
Material.of(tester.element(find.byType(RangeSlider))),
isNot(
paints
..circle(color: dragColor)
..path(color: Colors.black, style: PaintingStyle.stroke, strokeWidth: 2.0)
..path(color: Colors.black, style: PaintingStyle.stroke, strokeWidth: 2.0),
),
);
// Drag end thumb and return to original position.
final Offset bottomThumbLocation = tester
.getCenter(find.byType(RangeSlider))
.translate(220.0, 0.0);
final TestGesture dragEndThumb = await tester.startGesture(bottomThumbLocation);
await tester.pump(kPressTimeout);
await dragEndThumb.moveBy(const Offset(20.0, 0));
await tester.pump(kPressTimeout);
await dragEndThumb.moveBy(const Offset(-20.0, 0));
await dragEndThumb.up();
await tester.pumpAndSettle();
// Verify overlay disappears.
expect(
Material.of(tester.element(find.byType(RangeSlider))),
isNot(paints..circle(color: dragColor)),
);
// Hover on start thumb.
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(topThumbLocation);
await tester.pumpAndSettle();
// Verify overlay appears only for start thumb and no shadow is visible.
expect(
Material.of(tester.element(find.byType(RangeSlider))),
paints
..circle(color: hoverColor)
..path(color: Colors.black, style: PaintingStyle.stroke, strokeWidth: 2.0)
..path(color: Colors.black, style: PaintingStyle.stroke, strokeWidth: 2.0),
);
final RenderObject renderObject = tester.renderObject(find.byType(RangeSlider));
// 2 thumbs and 1 overlay.
expect(renderObject, paintsExactlyCountTimes(#drawCircle, 3));
// Move away from thumb
await gesture.moveTo(tester.getTopRight(find.byType(RangeSlider)));
await tester.pumpAndSettle();
// Verify overlay disappears
expect(
Material.of(tester.element(find.byType(RangeSlider))),
isNot(paints..circle(color: hoverColor)),
);
});
}