Skip to content

Commit 5e417dc

Browse files
authored
Fix RangeSlider dragged cursor handling. (flutter#179988)
Fixes flutter#179987 ### Description - Fixes dragged cursor handling in `RangeSlider` - Replaces `MaterialState` occurrences with `WidgetState` - Updates tests to ensure that all cases are covered | Before | After | | - | - | | <video src="https://github.com/user-attachments/assets/756b01e0-e52b-4280-af5e-ef02a6081855" /> | <video src="https://github.com/user-attachments/assets/5bd37714-740d-4056-ac7d-b57773a89e62" /> | ## 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. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent bc63dfe commit 5e417dc

File tree

2 files changed

+75
-58
lines changed

2 files changed

+75
-58
lines changed

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import 'package:flutter/widgets.dart';
2626
import 'color_scheme.dart';
2727
import 'constants.dart';
2828
import 'debug.dart';
29-
import 'material_state.dart';
3029
import 'range_slider_parts.dart';
3130
import 'slider_theme.dart';
3231
import 'slider_value_indicator_shape.dart';
@@ -567,15 +566,17 @@ class _RangeSliderState extends State<RangeSlider> with TickerProviderStateMixin
567566
}
568567

569568
void _handleDragStart(RangeValues values) {
570-
assert(widget.onChangeStart != null);
571-
_dragging = true;
572-
widget.onChangeStart!(_lerpRangeValues(values));
569+
setState(() {
570+
_dragging = true;
571+
});
572+
widget.onChangeStart?.call(_lerpRangeValues(values));
573573
}
574574

575575
void _handleDragEnd(RangeValues values) {
576-
assert(widget.onChangeEnd != null);
577-
_dragging = false;
578-
widget.onChangeEnd!(_lerpRangeValues(values));
576+
setState(() {
577+
_dragging = false;
578+
});
579+
widget.onChangeEnd?.call(_lerpRangeValues(values));
579580
}
580581

581582
// Returns a number between min and max, proportional to value, which must
@@ -764,8 +765,8 @@ class _RangeSliderState extends State<RangeSlider> with TickerProviderStateMixin
764765
textScaleFactor: effectiveTextScale,
765766
screenSize: screenSize(),
766767
onChanged: _enabled && (widget.max > widget.min) ? _handleChanged : null,
767-
onChangeStart: widget.onChangeStart != null ? _handleDragStart : null,
768-
onChangeEnd: widget.onChangeEnd != null ? _handleDragEnd : null,
768+
onChangeStart: _handleDragStart,
769+
onChangeEnd: _handleDragEnd,
769770
state: this,
770771
semanticFormatterCallback: widget.semanticFormatterCallback,
771772
hovering: _showHoverHighlight,
@@ -1759,7 +1760,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
17591760
textDirection: textDirection,
17601761
sliderTheme: thumbWidth != null && thumbHeight != null
17611762
? _sliderTheme.copyWith(
1762-
thumbSize: MaterialStatePropertyAll<Size?>(Size(thumbWidth, thumbHeight)),
1763+
thumbSize: WidgetStatePropertyAll<Size?>(Size(thumbWidth, thumbHeight)),
17631764
)
17641765
: _sliderTheme,
17651766
thumb: bottomThumb,
@@ -1848,7 +1849,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
18481849
textDirection: textDirection,
18491850
sliderTheme: thumbWidth != null && thumbHeight != null
18501851
? _sliderTheme.copyWith(
1851-
thumbSize: MaterialStatePropertyAll<Size?>(Size(thumbWidth, thumbHeight)),
1852+
thumbSize: WidgetStatePropertyAll<Size?>(Size(thumbWidth, thumbHeight)),
18521853
)
18531854
: _sliderTheme,
18541855
thumb: topThumb,

packages/flutter/test/material/range_slider_test.dart

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,7 @@ void main() {
15321532
);
15331533
}
15341534

1535-
await tester.pumpWidget(buildApp(divisions: 3));
1535+
await tester.pumpWidget(buildApp(divisions: 5));
15361536

15371537
final RenderObject valueIndicatorBox = tester.renderObject(find.byType(Overlay));
15381538
final Offset topRight = tester.getTopRight(find.byType(RangeSlider)).translate(-24, 0);
@@ -2416,52 +2416,52 @@ void main() {
24162416
});
24172417

24182418
testWidgets('RangeSlider WidgetStateMouseCursor resolves correctly', (WidgetTester tester) async {
2419-
var values = const RangeValues(50, 70);
2420-
const MouseCursor disabledCursor = SystemMouseCursors.basic;
2421-
const MouseCursor hoveredCursor = SystemMouseCursors.grab;
2419+
var values = const RangeValues(20, 75);
2420+
const MouseCursor systemDefaultCursor = SystemMouseCursors.basic;
2421+
const MouseCursor disabledCursor = SystemMouseCursors.forbidden;
24222422
const MouseCursor draggedCursor = SystemMouseCursors.move;
2423+
const MouseCursor hoveredCursor = SystemMouseCursors.grab;
24232424

24242425
Widget buildFrame({required bool enabled}) {
24252426
return MaterialApp(
24262427
home: Directionality(
24272428
textDirection: TextDirection.ltr,
24282429
child: Material(
2429-
child: StatefulBuilder(
2430-
builder: (BuildContext context, StateSetter setState) {
2431-
return Center(
2432-
child: MouseRegion(
2433-
cursor: SystemMouseCursors.forbidden,
2434-
child: RangeSlider(
2435-
mouseCursor: WidgetStateProperty.resolveWith<MouseCursor?>((
2436-
Set<WidgetState> states,
2437-
) {
2438-
if (states.contains(WidgetState.disabled)) {
2439-
return disabledCursor;
2440-
}
2441-
if (states.contains(WidgetState.dragged)) {
2442-
return draggedCursor;
2443-
}
2444-
if (states.contains(WidgetState.hovered)) {
2445-
return hoveredCursor;
2446-
}
2447-
2448-
return SystemMouseCursors.none;
2449-
}),
2450-
values: values,
2451-
max: 100.0,
2452-
onChanged: enabled
2453-
? (RangeValues newValues) {
2454-
setState(() {
2455-
values = newValues;
2456-
});
2457-
}
2458-
: null,
2459-
onChangeStart: enabled ? (RangeValues newValues) {} : null,
2460-
onChangeEnd: enabled ? (RangeValues newValues) {} : null,
2461-
),
2430+
child: Column(
2431+
mainAxisAlignment: MainAxisAlignment.center,
2432+
children: [
2433+
Center(
2434+
child: StatefulBuilder(
2435+
builder: (BuildContext context, StateSetter setState) {
2436+
return RangeSlider(
2437+
mouseCursor: WidgetStateProperty.resolveWith<MouseCursor?>((
2438+
Set<WidgetState> states,
2439+
) {
2440+
if (states.contains(WidgetState.disabled)) {
2441+
return disabledCursor;
2442+
}
2443+
if (states.contains(WidgetState.dragged)) {
2444+
return draggedCursor;
2445+
}
2446+
if (states.contains(WidgetState.hovered)) {
2447+
return hoveredCursor;
2448+
}
2449+
return SystemMouseCursors.click;
2450+
}),
2451+
values: values,
2452+
max: 100.0,
2453+
onChanged: enabled
2454+
? (RangeValues newValues) {
2455+
setState(() {
2456+
values = newValues;
2457+
});
2458+
}
2459+
: null,
2460+
);
2461+
},
24622462
),
2463-
);
2464-
},
2463+
),
2464+
],
24652465
),
24662466
),
24672467
),
@@ -2472,22 +2472,38 @@ void main() {
24722472
kind: PointerDeviceKind.mouse,
24732473
pointer: 1,
24742474
);
2475-
await gesture.addPointer(location: Offset.zero);
2475+
addTearDown(gesture.removePointer);
24762476

2477+
// System default.
2478+
await gesture.addPointer(location: Offset.zero);
24772479
await tester.pumpWidget(buildFrame(enabled: false));
2480+
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), systemDefaultCursor);
2481+
2482+
// Disabled.
2483+
await gesture.moveTo(tester.getCenter(find.byType(RangeSlider)));
2484+
await tester.pump();
24782485
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), disabledCursor);
24792486

2487+
// Hovered.
24802488
await tester.pumpWidget(buildFrame(enabled: true));
2481-
await gesture.moveTo(tester.getCenter(find.byType(RangeSlider))); // start hover
2482-
await tester.pumpAndSettle();
24832489
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), hoveredCursor);
24842490

2485-
await tester.timedDrag(
2486-
find.byType(RangeSlider),
2487-
const Offset(20.0, 0.0),
2488-
const Duration(milliseconds: 100),
2489-
);
2491+
// Dragged.
2492+
await gesture.down(tester.getCenter(find.byType(RangeSlider)));
2493+
await gesture.moveBy(const Offset(20.0, 0.0));
2494+
await tester.pump();
24902495
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), draggedCursor);
2496+
2497+
// Hovered.
2498+
await gesture.up();
2499+
await tester.pump();
2500+
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), hoveredCursor);
2501+
2502+
// System default.
2503+
await gesture.moveTo(Offset.zero);
2504+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
2505+
await tester.pump();
2506+
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), systemDefaultCursor);
24912507
});
24922508

24932509
testWidgets('RangeSlider can be hovered and has correct hover color', (

0 commit comments

Comments
 (0)