From f733e3a4415206553e7baba6986e5d1564769c08 Mon Sep 17 00:00:00 2001 From: yim Date: Sat, 9 Mar 2024 15:50:23 +0800 Subject: [PATCH] Fix multiple calls to Slider's onChanged. (#143680) Fixes #143524 --- packages/flutter/lib/src/material/slider.dart | 13 +++++++-- .../flutter/test/material/slider_test.dart | 28 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart index 0719926cf6f..d1e986e0324 100644 --- a/packages/flutter/lib/src/material/slider.dart +++ b/packages/flutter/lib/src/material/slider.dart @@ -612,6 +612,11 @@ class _SliderState extends State with TickerProviderStateMixin { bool _dragging = false; + // For discrete sliders, _handleChanged might receive the same value + // multiple times. To avoid calling widget.onChanged repeatedly, the + // value from _handleChanged is temporarily saved here. + double? _currentChangedValue; + FocusNode? _focusNode; FocusNode get focusNode => widget.focusNode ?? _focusNode!; @@ -664,8 +669,11 @@ class _SliderState extends State with TickerProviderStateMixin { void _handleChanged(double value) { assert(widget.onChanged != null); final double lerpValue = _lerp(value); - if (lerpValue != widget.value) { - widget.onChanged!(lerpValue); + if (_currentChangedValue != lerpValue) { + _currentChangedValue = lerpValue; + if (_currentChangedValue != widget.value) { + widget.onChanged!(_currentChangedValue!); + } } } @@ -676,6 +684,7 @@ class _SliderState extends State with TickerProviderStateMixin { void _handleDragEnd(double value) { _dragging = false; + _currentChangedValue = null; widget.onChangeEnd?.call(_lerp(value)); } diff --git a/packages/flutter/test/material/slider_test.dart b/packages/flutter/test/material/slider_test.dart index 34c50025c56..97c07817f93 100644 --- a/packages/flutter/test/material/slider_test.dart +++ b/packages/flutter/test/material/slider_test.dart @@ -4253,4 +4253,32 @@ void main() { ); }); }); + + // This is a regression test for https://github.com/flutter/flutter/issues/143524. + testWidgets('Discrete Slider.onChanged is called only once', (WidgetTester tester) async { + int onChangeCallbackCount = 0; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: Slider( + max: 5, + divisions: 5, + value: 0, + onChanged: (double newValue) { + onChangeCallbackCount++; + }, + ), + ), + ), + ), + ); + + final TestGesture gesture = await tester.startGesture(tester.getTopLeft(find.byType(Slider))); + await tester.pump(kLongPressTimeout); + await gesture.moveBy(const Offset(160.0, 0.0)); + await gesture.moveBy(const Offset(1.0, 0.0)); + await gesture.moveBy(const Offset(1.0, 0.0)); + expect(onChangeCallbackCount, 1); + }); }