Skip to content

ReorderableListView#onReorder passes an unexpected new index #24786

Description

@chrislambe

The ReorderableListView widget seems to passing an incorrect newIndex parameter when triggering the onReorder handler.

Steps to Reproduce

Here's a demo of the behavior as described in the below steps:
https://gist.github.com/chrislambe/b9aa44a5f3d3cc7dc6fdb24fb221982d

  1. Create an app with a ReorderableListView widget and dummy data.
  2. In the onReorder handler, print out the new index.
  3. Drag the item in the 0 index position down until it swaps positions with the item in 1 index then, without releasing the item, drag it back to its original position and release. Note that the printed new index is 1 instead of 0, even though the intent was to drop the item in its original position.
  4. Drag the item to the bottom of the list and release. Note that the new index is 1 greater than the bounds of the children List.

Logs

Result of above step 3

flutter: oldIndex: 0, newIndex: 1, length: 4

Result of above step 4

flutter: oldIndex: 0, newIndex: 4, length: 4

flutter analyze

$ flutter analyze
Analyzing on_reorder_broken...
No issues found! (ran in 2.3s)

flutter doctor -v

$ flutter doctor -v
[✓] Flutter (Channel dev, v0.11.9, on Mac OS X 10.14.1 18B75, locale en-US)
    • Flutter version 0.11.9 at /Applications/flutter
    • Framework revision d48e6e433c (7 days ago), 2018-11-20 22:05:23 -0500
    • Engine revision 5c8147450d
    • Dart version 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
    • Android SDK at /Users/chris/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-27, build-tools 27.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b08)
    • All Android licenses accepted.

[!] iOS toolchain - develop for iOS devices (Xcode 10.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 10.1, Build version 10B61
    ✗ Verify that all connected devices have been paired with this computer in Xcode.
      If all devices have been paired, libimobiledevice and ideviceinstaller may require updating.
      To update with Brew, run:
        brew update
        brew uninstall --ignore-dependencies libimobiledevice
        brew uninstall --ignore-dependencies usbmuxd
        brew install --HEAD usbmuxd
        brew unlink usbmuxd
        brew link usbmuxd
        brew install --HEAD libimobiledevice
        brew install ideviceinstaller
    • ios-deploy 1.9.2
    ✗ ios-deploy out of date (1.9.4 is required). To upgrade with Brew:
        brew upgrade ios-deploy
    • CocoaPods version 1.5.3

[✓] Android Studio (version 3.0)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 23.2.1
    • Dart plugin version 171.4424
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b08)

[✓] VS Code (version 1.29.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 2.20.0

[✓] Connected device (2 available)
    • moto x4   • ZY224KS2L3                           • android-arm64 • Android 8.1.0 (API 27)
    • iPhone XR • F2133438-AFB5-421D-9CC0-36FD9FE97D28 • ios           • iOS 12.1 (simulator)

! Doctor found issues in 1 category.

Activity

  1. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    p: material_uimaterial_ui package in flutter/packages
    on Nov 27, 2018
  2. added this to the milestone on Nov 27, 2018
  3. jason-simmons commented on Nov 30, 2018

    @jason-simmons
    Member
  4. Daniel-BD commented on Oct 25, 2019

    @Daniel-BD

    I'm having the exact same issue, almost a year after this issue was opened. I'm running stable channel, v1.9.1+hotfix.2

    Is this being worked on? Seems like this widget is pretty much useless without this being fixed :)

  5. ffeu commented on Oct 25, 2019

    @ffeu

    There's an easy workaround.

    Check this out:
    https://stackoverflow.com/a/54164333/796963

  6. fellow7000 commented on Jan 18, 2020

    @fellow7000

    Funny (not really) that more than one year later this simple bug is still in the framework,,,

    Hello, Flutter Team, are you going to do anything on that?..

  7. esDotDev commented on Feb 19, 2020

    @esDotDev

    The workaround is a pretty big hack, and still doesn't work 100%. This really needs to get fixed.

  8. TahaTesser commented on May 11, 2020

    @TahaTesser
    Contributor
    flutter doctor -v
    [✓] Flutter (Channel dev, 1.19.0-0.0.pre, on Mac OS X 10.15.4 19E287, locale
        en-GB)
        • Flutter version 1.19.0-0.0.pre at /Users/tahatesser/Code/flutter_dev
        • Framework revision a849daf283 (3 days ago), 2020-05-07 18:59:02 -0700
        • Engine revision 3953c3ccd1
        • Dart version 2.9.0 (build 2.9.0-5.0.dev 4da5b40fb6)
    
     
    [✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
        • Android SDK at /Users/tahatesser/Code/SDK
        • Platform android-29, build-tools 29.0.3
        • ANDROID_HOME = /Users/tahatesser/Code/SDK
        • Java binary at: /Applications/Android
          Studio.app/Contents/jre/jdk/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build
          1.8.0_212-release-1586-b4-5784211)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 11.4.1)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Xcode 11.4.1, Build version 11E503a
        • CocoaPods version 1.9.1
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 3.6)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin version 45.1.1
        • Dart plugin version 192.7761
        • Java version OpenJDK Runtime Environment (build
          1.8.0_212-release-1586-b4-5784211)
    
    [✓] VS Code (version 1.45.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.10.1
    
    [✓] Connected device (4 available)
        • Android SDK built for x86 • emulator-5554 • android-x86    • Android 10
          (API 29) (emulator)
        • macOS                     • macOS         • darwin-x64     • Mac OS X
          10.15.4 19E287
        • Web Server                • web-server    • web-javascript • Flutter Tools
        • Chrome                    • chrome        • web-javascript • Google Chrome
          81.0.4044.138
    
    • No issues found!
    
    
    
  9. added
    P2Important issues not at the top of the work list
    on May 29, 2020
  10. vagaone commented on Jun 8, 2020

    @vagaone

    I digged a bit into it. The indices in reorderable_list.dart are mainly used for the UI handling of the droppable space and don't represent the array position of the elements being dragged. But, and that is the problem, the indices are then used for the reorder callback.

        // Places the value from startIndex one space before the element at endIndex.
        void reorder(int startIndex, int endIndex) {
          setState(() {
            if (startIndex != endIndex)
              widget.onReorder(startIndex, endIndex);
            // Animates leftover space in the drop area closed.
            // TODO(djshuckerow): bring the animation in line with the Material
            // specifications.
            _ghostController.reverse(from: 0.1);
            _entranceController.reverse(from: 0.1);
            _dragging = null;
          });
        }
    

    A quick fix for the issue would be to decrease the endIndex if it is larger then the startIndex:

        void reorder(int startIndex, int endIndex) {
          setState(() {
            if (startIndex != endIndex) {
              if(endIndex > startIndex) {
                endIndex -= 1;
              }
              widget.onReorder(startIndex, endIndex);
            }
              
            // Animates leftover space in the drop area closed.
            // TODO(djshuckerow): bring the animation in line with the Material
            // specifications.
            _ghostController.reverse(from: 0.1);
            _entranceController.reverse(from: 0.1);
            _dragging = null;
          });
        }
    

    Can anyone take a look and give some thought's on that? I don't think it is a really nice solution, but it works.

  11. 22 remaining items

  12. araruna commented on May 28, 2021

    @araruna

    I believe that the 'most promising' interpretation for the newIndex should be that it represents the old index of the item that is now after the moved item. Then, the only annoying situation seems to be when we drag an item down and then place it back, which triggers the onReorder unnecessarily...

    This even applies to the OP's case and seems to make sense of why sometimes this value is equal to the List's length (which would mean that none of the items is now after the moved item).

    At least this is what seems to happen to me using the current version.

    $ flutter --version
    
    Flutter 2.2.1 • channel stable • https://github.com/flutter/flutter.git
    Framework • revision 02c026b03c (8 hours ago) • 2021-05-27 12:24:44 -0700
    Engine • revision 0fdb562ac8
    Tools • Dart 2.13.1
    
  13. ronytesler commented on Jul 12, 2021

    @ronytesler

    Any working workaround on web?!

  14. ronytesler commented on Jul 12, 2021

    @ronytesler

    will it be correct to reduce 1 from newIndex if the item was moved from a lower index?

  15. kenthinson commented on Jul 23, 2021

    @kenthinson

    @ronytesler

    This is what I'm using that seems to be working in my testing.

    
                                    array.insert(newIndex, array[oldIndex]);
                                if (oldIndex < newIndex) {
                                  array.removeAt(oldIndex);
                                } else {
                                  array.removeAt(oldIndex + 1);
                                }
                                setState(() {});
    
  16. pitazzo commented on Jul 27, 2021

    @pitazzo

    I can't believe this bug is 2 years old

  17. Flucadetena commented on Sep 22, 2021

    @Flucadetena

    Same Issue, in my case when you move a Widget "higher" in the list, from 0 to 1, the "newIdx" comes wrong with a "+1". So instead of 1 you get 2. But when you move the widget "down" in the list, the values come wright. So my code looks like this:

    onReorder: (oldIdx, newIdx) {
    
            Widget wid = widgets.removeAt(oldIdx);
            if (newIdx > oldIdx)
              widgets.insert(newIdx - 1, wid);
            else
              widgets.insert(newIdx, wid);
    
            setState(() {});
          },
    
    
  18. banderberg commented on Oct 14, 2021

    @banderberg

    Just discovered this bug for myself this morning. Surely the fix can't be very difficult to implement.

  19. added
    c: API breakBackwards-incompatible API changes
    customer: crowdAffects or could affect many people, though not necessarily a specific customer.
    on Nov 5, 2021
  20. changed the title [-]ReorderableListView#onReorder passes an incorrect new index[/-] [+]ReorderableListView#onReorder passes an unexpected new index[/+] on Nov 5, 2021
  21. HansMuller commented on Feb 8, 2022

    @HansMuller
    Contributor

    We've decided not to correct this issue because the obvious fix introduces a backwards incompatibility that can't be automatically corrected. More here: #93146 (comment)

    I apologize for not resolving this quickly, when it would have been possible to just make the break.

  22. github-actions commented on Feb 22, 2022

    @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.

  23. locked as resolved and limited conversation to collaborators on Feb 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

P2Important issues not at the top of the work lista: qualityA truly polished experiencec: API breakBackwards-incompatible API changescustomer: crowdAffects or could affect many people, though not necessarily a specific customer.f: scrollingViewports, list views, slivers, etc.found 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/packages

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions