Skip to content

Slider notifies "onChangeEnd" twice when it is over a InkWell/GestureDetector #28115

Description

@shinayser

When embedding a Slider inside a InkWell, the Slider fires twice "onChangeEnd" if you start dragging it.
Check this snippet:

void main() => runApp(MyAppTeste());

class MyAppTeste extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Tests Flutter',
      home: Page(),
    );
  }
}

class Page extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          InkWell(
            onTap: () {
              print("Tapped Inkwell!");
            },
            child: SliderTest(),
          ),
//          SliderTest()
        ],
      ),
    );
  }
}

class SliderTest extends StatefulWidget {
  @override
  _SliderTestState createState() => _SliderTestState();
}

class _SliderTestState extends State<SliderTest> {
  double value = 0;

  @override
  Widget build(BuildContext context) {
    return Slider(
      value: value,
      min: 0,
      max: 100,
      onChanged: (value) {
        setState(() {
          this.value = value;
        });
      },
      onChangeEnd: (value) {
        print("Value selected $value");
      },
    );
  }
}

If you just tap somewhere in the slider's "line" verything works fine. But if you drag it, it will fire twict the "onChanceEnd" method.

I also realized the if you drag too fast, it will fires only once. But users don't use the sliders like that. I believe this has something related to the InkWell/GestureDetector that is behind it....

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, v1.0.0, on Microsoft Windows [versão 10.0.17763.316], locale pt-BR)
[√] Android toolchain - develop for Android devices (Android SDK 28.0.3)
[√] Android Studio (version 3.3)
[√] IntelliJ IDEA Community Edition (version 2018.3)
[√] VS Code (version 1.30.1)
[√] Connected device (1 available)

• No issues found!

Activity

  1. changed the title [-]Slider notifies twitch "onChangeEnd" when over a InkWell/GestureDetector[/-] [+]Slider notifies twice "onChangeEnd" when over a InkWell/GestureDetector[/+] on Feb 18, 2019
  2. changed the title [-]Slider notifies twice "onChangeEnd" when over a InkWell/GestureDetector[/-] [+]Slider notifies "onChangeEnd" twice when it is over a InkWell/GestureDetector[/+] on Feb 18, 2019
  3. zoechi commented on Feb 19, 2019

    @zoechi
    Contributor

    To investigate we would need a minimal runnable reproduction
    as a single file so that we can just copy your code into lib/main.dart of a new project and run to reproduce.

  4. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Feb 19, 2019
  5. shinayser commented on Feb 19, 2019

    @shinayser
    Author

    To investigate we would need a minimal runnable reproduction
    as a single file so that we can just copy your code into lib/main.dart of a new project and run to reproduce.

    Zoechi the one the post is exactly that. Actually this is a Scaffold. Just embed it into any MaterialApp will run properly.

  6. zoechi commented on Feb 19, 2019

    @zoechi
    Contributor

    To be runnable it should include main() {...}.
    The point is that we shouldn't need to make any assumptions. Just copy-paste and run.
    Otherwise there is always room for different behavior.

  7. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Feb 19, 2019
  8. added this to the milestone on Feb 19, 2019
  9. shinayser commented on Feb 19, 2019

    @shinayser
    Author

    Understood! I've updated the code above with the requirements.

  10. zoechi commented on Feb 19, 2019

    @zoechi
    Contributor

    Thanks!

    I wasn't able to reproduce though.

    [✓] Flutter (Channel master, v1.2.2-pre.21, on Mac OS X 10.14.3 18D42, locale en-AT)
    [✓] Connected device (1 available)
        • Android SDK built for x86 • emulator-5554 • android-x86 • Android 9 (API 28) (emulator)
    

    Can you please try with master (or dev) channel?

    flutter channel master
    flutter doctor
    
  11. shinayser commented on Feb 19, 2019

    @shinayser
    Author

    I just switched to 1.2.2-pre.21 and seems the problem is gone. You may close the issue if you want :)
    Also, this issue seems related to this same problem: #18561

    Edit: Actually it is not gone, check comment below.

  12. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Feb 19, 2019
  13. shinayser commented on Feb 19, 2019

    @shinayser
    Author

    @zoechi Actually I've posted a wrong code snippet on this issue and tested it on my machine wrongly too... My bad!
    The issue still persist on v1.2.2-pre.21.
    I've updated the code snippet above so you can check for yourself.

  14. reopened this on Feb 19, 2019
  15. 41 remaining items

  16. WieFel commented on Jan 21, 2021

    @WieFel

    Same issue. Running Flutter stable 1.22.5

    This problem doesn't arise in CupertinoSlider!
    CupertinoSlider differs from Slider in that it doesn't define a TapGestureRecognizer:

    Slider:

    _drag = HorizontalDragGestureRecognizer()
      ..team = team
      ..onStart = _handleDragStart
      ..onUpdate = _handleDragUpdate
      ..onEnd = _handleDragEnd
      ..onCancel = _endInteraction;
    _tap = TapGestureRecognizer()
      ..team = team
      ..onTapDown = _handleTapDown
      ..onTapUp = _handleTapUp
      ..onTapCancel = _endInteraction;

    CupertinoSlider:

    _drag = HorizontalDragGestureRecognizer()
      ..onStart = _handleDragStart
      ..onUpdate = _handleDragUpdate
      ..onEnd = _handleDragEnd;

    So the problem is probably related to Slider reacting to the initial "tap" when beginning to drag the slider thumb. In that moment, onChangeEnd is called the first time. When the dragging really ends, it is called the second time.

    Finding a workaround

    I wrapped my Slider with a GestureDetector and started checking which events are triggered:

    GestureDetector(
      onHorizontalDragCancel: () => print("GESTURE DETECTOR onHorizontalDragCancel"),
      child: Slider(
        ...
        value: ...,
        onChanged: ...,
        onChangeStart: (value) {
          print("SLIDER onChangeStart");
        },
        onChangeEnd: (value) {
          print("SLIDER onChangeEnd");
        },
      ),
    )

    Then, I identified the following patterns:

    1. When simply clicking somewhere on the slider:
    flutter: GESTURE DETECTOR onHorizontalDragCancel
    flutter: SLIDER onChangeStart
    flutter: SLIDER onChangeEnd
    
    1. When dragging the slider thumb somewhere:
    flutter: SLIDER onChangeStart
    flutter: GESTURE DETECTOR onHorizontalDragCancel
    flutter: SLIDER onChangeEnd
    flutter: SLIDER onChangeStart
    flutter: SLIDER onChangeEnd
    

    I identified that the onHorizontalDragCancel that is triggered in 2. after the first onChangeStart can be helpful.
    So I came up with the following workaround.

    My workaround

    I use a property _cancelled within my State in order to check whether onHorizontalDragCancel has been triggered between onChangeStart and onChangeEnd. If yes, then the call to onChangeEnd is ignored!

    bool _cancelled = false;
    
    ...
    
    GestureDetector(
      onHorizontalDragCancel: () => _cancelled = true,
      child: Slider(
        ...
        value: ...,
        onChanged: ...,
        onChangeStart: (value) => _cancelled = false,
        onChangeEnd: (value) {
          if (_cancelled) return;
          // do onChangeEnd stuff
        },
      ),
    )

    Now, the slider's onChangeEnd is called only once! Enjoy!

  17. daadu commented on Mar 15, 2021

    @daadu
    Contributor

    @WieFel Thanks for the workaround!!

    I am facing this in v2 too.

  18. Sakorni commented on Mar 17, 2021

    @Sakorni

    @WieFel, Thanks for your workaround! You're breathtaking!

  19. maares commented on Mar 21, 2021

    @maares

    Unfortunately for me non-of above solutions worked for me, although they worked as great examples for my work around:

    
    bool _cancelBugEvent = true;
    
    Slider(
              min: 0.0,
              max: 1.0,
              onChanged: (value) async {
                
              },
              onChangeStart: (value) async {
               
              },
              onChangeEnd: (value) async {
                ///FIXME: https://github.com/flutter/flutter/issues/28115
                
                if (_cancelBugEvent) {
                  _cancelBugEvent = false;
                } else if (!_cancelBugEvent) {
                  _cancelBugEvent = true;
                  //execute code when drag released
                }
                
              };
    
    

    Is there a release plan we can track? Thanks in advance!

  20. cirediew commented on Mar 24, 2021

    @cirediew

    I'm using Slider.adaptive and this problem only occurs on Android (material slider) devices. The cupertino version of the adaptive slider works fine.

    This makes it even harder because none of the fixes mentioned above work without breaking the other platforms widget.

    flutter doctor
    [✓] Flutter (Channel stable, 2.0.2, on macOS 11.1 20C69 darwin-x64, locale en-GB)
        • Flutter version 2.0.2 at /Users/user/development/flutter
        • Framework revision 8962f6dc68 (13 days ago), 2021-03-11 13:22:20 -0800
        • Engine revision 5d8bf811b3
        • Dart version 2.12.1
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
        • Android SDK at /Users/user/Library/Android/sdk
        • Platform android-30, build-tools 30.0.3
        • Java binary at: /Users/user/Library/Application
          Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.7199119/Android
          Studio.app/Contents/jre/jdk/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Xcode 12.4, Build version 12D4e
        • CocoaPods version 1.10.1
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 4.1)
        • Android Studio at /Users/user/Library/Application
          Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.7199119/Android Studio.app/Contents
        • Flutter plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/9212-flutter
        • Dart plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/6351-dart
        • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    
  21. evanwsu commented on Apr 5, 2021

    @evanwsu

    @WieFel This workaround is not so reliable.
    I think a correct flow is onChangeStart -> onChanged -> onChangeEnd, except for onChanged the other methods can only appear once.

    Most of the current cases are fine, we can also set a _complete flag to control the number of appearances of onChangeStart.

    onHorizontalDragCancel: () {
      _cancelled = true;
      print('onHorizontalDragCancel');
    }
    
    onChangeStart: (double value) {
      if(_complete){
        print('onChangeStart');
      }
      _complete = false;
      _cancelled = false;
      },
    onChanged: () => print('onChanged');,
    onChangeEnd: (double value) {
      if(_cancelled) return;
      _complete = true;
      print('onChangeEnd')
      }
    

    If you drop the area outside the thumb, then pause for a moment and continue sliding, the print log looks like this

    onChangeStart: 0.48814937736414643
    onChanged: 0.29379905024661296
    onChangeEnd: 0.29379905024661296
    onHorizontalDragCancel
    onChangeStart: 0.29379905024661296
    onChanged: 0.2353359415561338
    onChanged: 0.23375586014759692
    onChanged: 0.23217577873906003
    onChangeEnd: 0.23059569733052315
    

    dh_slider uses a delay as a workaround, it's not a great way to do it, but it's safer.

  22. flaviopessini commented on Apr 19, 2021

    @flaviopessini

    Hello, I am experiencing the same problem with Slider in version 2.0.5 of Flutter. The function is triggered twice.

  23. JKRC commented on May 10, 2021

    @JKRC

    I have the same problem :(( any solution for this?

  24. added
    r: fixedIssue is closed as already fixed in a newer version
    on May 21, 2021
  25. delegate-it commented on Jul 2, 2021

    @delegate-it

    I am also having a problem with the slider. I just added a fix on the update to check the current value and the old value are not the same, then I will send the update to the cloud.
    But now I am facing another problem also related to the same issue. I realised the problem is happening because I am not tapping on the center of the slider. If I tap exactly on the same, then the onchangedEnd event fires with the same value otherwise I am get a different value. One at the start and another event at the end.

  26. github-actions commented on Jul 30, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  27. locked as resolved and limited conversation to collaborators on Jul 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listcustomer: crowdAffects or could affect many people, though not necessarily a specific customer.found in release: 1.22Found to occur in 1.22found in release: 1.26Found to occur in 1.26frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packagesr: fixedIssue is closed as already fixed in a newer version

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions