-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Fix: Flicker when reorderable list doesn't change its position #151026
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Flicker when reorderable list doesn't change its position #151026
Conversation
|
It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact "@test-exemption-reviewer" in the #hackers channel in Chat (don't just cc them here, they won't see it! Use Discord!). If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix? Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
|
@rkishan516 Thank you for the contribution. It looks like it is failing some checks. Can you please take a look? Also, as per the bot message above: Can you add a test for this so we never regress this in the future? Thank you. |
|
@goderbauer Sure, I will check failing checks and add tests. |
|
@goderbauer I have added regression test for this PR, but need some help with other failing test. |
|
Here are some resources that may be of help: https://github.com/flutter/flutter/blob/master/docs/contributing/testing/Running-and-writing-tests.md |
09099f1 to
3e2507b
Compare
|
@goderbauer I have fixed the tests, But I might have messed up in code quality. Can you please review ? |
ad0ce2d to
20b96cb
Compare
|
Hi @Piinks Can you take a look at this pr? feel free to reassign if not. |
20b96cb to
6b6a21e
Compare
0a3ed86 to
df87d63
Compare
0535985 to
42dc0ea
Compare
| expect(tester.getTopLeft(find.text('item 0')), const Offset(0, 500)); | ||
| }); | ||
|
|
||
| testWidgets('SliverReorderableList - properly animates the drop at starting position', (WidgetTester tester) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a similar test where reverse: true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Piinks I have pushed the required test.
| int newIndex = _insertIndex!; | ||
| for (final _ReorderableItemState item in _items.values) { | ||
| if (item.index == _dragIndex! || !item.mounted) { | ||
| if ((_reverse && item.index == _dragIndex!) || !item.mounted) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a test in reverse as well to cover this case.
d7e2805 to
5c4042b
Compare
5c4042b to
585b3c8
Compare
Piinks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rkishan516 this looks just about ready to land, can you check the changes in the dropdown menu file? Maybe they were pulled in accidentally from a merge.
| int? search(List<DropdownMenuEntry<T>> entries, TextEditingController textEditingController) { | ||
| final String searchText = textEditingController.value.text.toLowerCase(); | ||
| int? search(List<DropdownMenuEntry<T>> entries) { | ||
| final String searchText = _localTextEditingController!.value.text.toLowerCase(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rkishan516 should these changes be here? They appear unrelated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't know, How I messed it up today. I have fixed and pushed.
c8e2b63 to
5a0a5f1
Compare
Piinks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you!
QuncCccccc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with nits:) Thanks for the contribution!
| } else { | ||
| if (itemStart <= proxyItemStart && proxyItemStart <= itemMiddle) { | ||
| if (item.index == _dragIndex!) { | ||
| // If end of the proxy is in not in ending half of item |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo?
| // If end of the proxy is in not in ending half of item | |
| // If end of the proxy is not in ending half of item, |
| if (itemStart <= proxyItemStart && proxyItemStart <= itemMiddle) { | ||
| if (item.index == _dragIndex!) { | ||
| // If end of the proxy is in not in ending half of item | ||
| // We don't process, because its drag item index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // We don't process, because its drag item index | |
| // we don't process, because its drag item index. |
This comment looks a little bit confusing, could you help double check? "because it's the original dragged item"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@QuncCccccc I have pushed the required changes.
5a0a5f1 to
93a2ca0
Compare
283d5f3 to
f3a82d7
Compare
flutter/flutter@303f222...2d30fe4 2024-09-13 [email protected] Roll Packages from 91caa7a to 330581f (4 revisions) (flutter/flutter#155171) 2024-09-13 [email protected] Fix: Flicker when reorderable list doesn't change its position (flutter/flutter#151026) 2024-09-13 [email protected] Stop reading .packages from flutter_tools. (flutter/flutter#154912) 2024-09-13 [email protected] Roll Flutter Engine from 70109e3b40c0 to bef48e87f438 (1 revision) (flutter/flutter#155156) 2024-09-13 [email protected] Roll Flutter Engine from d917a15823f3 to 70109e3b40c0 (1 revision) (flutter/flutter#155151) 2024-09-13 [email protected] Roll Flutter Engine from 94696ed75dea to d917a15823f3 (1 revision) (flutter/flutter#155147) 2024-09-13 [email protected] Fix TextField content should be selected on desktop when gaining focus (flutter/flutter#154916) 2024-09-13 [email protected] Roll Flutter Engine from 04802b779045 to 94696ed75dea (1 revision) (flutter/flutter#155144) 2024-09-13 [email protected] Roll Flutter Engine from 3d8163f47919 to 04802b779045 (2 revisions) (flutter/flutter#155138) 2024-09-13 [email protected] Roll Flutter Engine from 4d5fea97e933 to 3d8163f47919 (1 revision) (flutter/flutter#155136) 2024-09-13 [email protected] Mark `_LayoutBuilderElement` as always clean (flutter/flutter#154694) 2024-09-13 [email protected] Roll Flutter Engine from 8609af642725 to 4d5fea97e933 (7 revisions) (flutter/flutter#155134) 2024-09-12 [email protected] Disable fuchsia in flutter_tools (flutter/flutter#155111) 2024-09-12 [email protected] Address frame policy benchmark flakes (flutter/flutter#155130) 2024-09-12 [email protected] Roll Flutter Engine from 48ddaf578fb0 to 8609af642725 (11 revisions) (flutter/flutter#155128) 2024-09-12 49699333+dependabot[bot]@users.noreply.github.com Bump peter-evans/create-pull-request from 7.0.1 to 7.0.2 (flutter/flutter#155126) 2024-09-12 [email protected] Prevent the keyboard from reshowing on iOS (flutter/flutter#154584) 2024-09-12 [email protected] fix(Linux): specify application id (flutter/flutter#154522) 2024-09-12 [email protected] update changelog on master (flutter/flutter#155109) 2024-09-12 [email protected] iOS: update provisioning profile for 2024-2025 cert (flutter/flutter#155101) 2024-09-12 [email protected] Roll Packages from 4c18648 to 91caa7a (2 revisions) (flutter/flutter#155103) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
_**Note:** Alongside this PR, I've also prepared [another PR](#172882) with an alternative solution involving a more substantial refactor that addresses the root cause, rather than adding more conditional logic._ ## Description This PR fixes the proxy animation bug where dragging a `ReorderableList` item downward and then back to its original position causes it to animate to the wrong location (one position too low). ## The Problem When dragging a `ReorderableList` item downward and then back to its original position, the proxy widget briefly animates to the wrong location (one position too low) before snapping to the correct spot. **Reproduction**: Drag any item down past at least one other item, then drag it back to where it started. <p align="center"> <img src="https://github.com/user-attachments/assets/d0931dff-5600-441c-8536-2c61789767d0" alt="demo2" width="250"> </p> ## Root Cause This bug is specific to dragging an item down and then bringing it back up to nearly (but not 100% of the way ) to its original position: 1. When the item approaches its original position **from below**, `_insertIndex` becomes `item.index + 1` - This happens because Flutter's `ReorderableList` calculates `_insertIndex` with the dragged item still present in the list (see #24786) 2. The proxy _should_ animate to the item's original position at `item.index` - _But the proxy actually animates one position too low._ - This happens because `_dragEnd` incorrectly calculates `_finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(...)` - The `_extentOffset(...)` addition, designed for items dropping _between other items_, shifts the position down by one item's height - The correct calculation for "returning home from below" should be just `_itemOffsetAt(_insertIndex! - 1)` Note that this only occurs when returning from below (`_insertIndex > item.index`). Dragging upward (in a vertical list for example) or doesn't trigger this bug. ## Existing Implementation The existing `_dragEnd` method in `reorderable_list.dart`: ```dart void _dragEnd(_DragInfo item) { setState(() { if (_insertIndex == item.index) { _finalDropPosition = _itemOffsetAt(_insertIndex!); } else if (_reverse) { if (_insertIndex! >= _items.length) { _finalDropPosition = _itemOffsetAt(_items.length - 1) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex!) + _extentOffset(_itemExtentAt(_insertIndex!), _scrollDirection); } } else { if (_insertIndex! == 0) { _finalDropPosition = _itemOffsetAt(0) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(_itemExtentAt(_insertIndex! - 1), _scrollDirection); } } }); } ``` When returning from below, the code falls through to the final else block, which incorrectly adds `_extentOffset`. ## Fix Detect when `_insertIndex - item.index == 1` (indicating a return to original position from below) and animate to the correct position. ```dart if (_insertIndex! - item.index == 1) { // Drop at the original position when item returns from below _finalDropPosition = _itemOffsetAt(_insertIndex! - 1); } ``` This fix was proposed by @frankpape in #90856 (comment); I've merely validated and researched the background of why the fix works, and supported it with tests. **_Demo of the fixed implementation:_** <p align="center"> <img src="https://github.com/user-attachments/assets/a53e8920-ebca-4326-abe9-3b43b34419e5" alt="fixed" width="250"> </p> Fixes #88331 Fixes #90856 Fixes #150843 ## A note about a previous PR: While investigating this issue, I found a PR addressing what seemed to be [the same exact issue](#150843): PR #151026; it turns out that that PR solved a _portion_ of the edge case: the case where an item is dragged down and back and slightly **overshoots** its original position when being dragged back & dropped—but that PR did not account for the presence of this bug when the dragged item slightly **undershoots** its original position on the return drag. This new PR effectively addresses the 'undershooting' case. With this, I've added a new pair of regression tests that are identical to the [previous PR's tests](https://github.com/flutter/flutter/blob/master/packages/flutter/test/widgets/reorderable_list_test.dart#L734), except for the fact that they simulate an undershoot on the return trip (90% of the way back instead of 110% like the original tests). This definitively captures the issue, failing in the master branch and passing in this PR's branch. Here is the specific case resolved by the [**old** PR](#151026): <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/b0ddc745-6e9e-4f12-97da-454e2e76b06d" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/03e181fa-f43b-4405-b0c0-16d3465ad990" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Here is the additional case resolved by **this** PR: <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/9b4bb591-aa2f-4cf0-88b8-a3ec32b0f0ac" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/31646e9c-78f4-4252-921f-53583193868f" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Two final observations worth noting: - The fix proposed in this PR seems to **supersede** the previous PR's solution; it addresses both cases (overshooting and undershooting) even in my tests with the [original PR's changes ](https://github.com/flutter/flutter/pull/151026/files#diff-23a4bb073009d89f09084bdf5f85232de135b8f11be625e6312bb85900a90e67) reverted. Probably best to keep the old PR's code anyway to be conservative, but noteworthy. - I also found it notable that neither this PR nor the older PR fix any issue with "reversed lists", which, in my tests, are simply not subject to this edge case as we've defined it. The regression tests added for the reverse case are thus purely precautionary. ## 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. <!-- 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
…172380) _**Note:** Alongside this PR, I've also prepared [another PR](flutter#172882) with an alternative solution involving a more substantial refactor that addresses the root cause, rather than adding more conditional logic._ ## Description This PR fixes the proxy animation bug where dragging a `ReorderableList` item downward and then back to its original position causes it to animate to the wrong location (one position too low). ## The Problem When dragging a `ReorderableList` item downward and then back to its original position, the proxy widget briefly animates to the wrong location (one position too low) before snapping to the correct spot. **Reproduction**: Drag any item down past at least one other item, then drag it back to where it started. <p align="center"> <img src="https://github.com/user-attachments/assets/d0931dff-5600-441c-8536-2c61789767d0" alt="demo2" width="250"> </p> ## Root Cause This bug is specific to dragging an item down and then bringing it back up to nearly (but not 100% of the way ) to its original position: 1. When the item approaches its original position **from below**, `_insertIndex` becomes `item.index + 1` - This happens because Flutter's `ReorderableList` calculates `_insertIndex` with the dragged item still present in the list (see flutter#24786) 2. The proxy _should_ animate to the item's original position at `item.index` - _But the proxy actually animates one position too low._ - This happens because `_dragEnd` incorrectly calculates `_finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(...)` - The `_extentOffset(...)` addition, designed for items dropping _between other items_, shifts the position down by one item's height - The correct calculation for "returning home from below" should be just `_itemOffsetAt(_insertIndex! - 1)` Note that this only occurs when returning from below (`_insertIndex > item.index`). Dragging upward (in a vertical list for example) or doesn't trigger this bug. ## Existing Implementation The existing `_dragEnd` method in `reorderable_list.dart`: ```dart void _dragEnd(_DragInfo item) { setState(() { if (_insertIndex == item.index) { _finalDropPosition = _itemOffsetAt(_insertIndex!); } else if (_reverse) { if (_insertIndex! >= _items.length) { _finalDropPosition = _itemOffsetAt(_items.length - 1) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex!) + _extentOffset(_itemExtentAt(_insertIndex!), _scrollDirection); } } else { if (_insertIndex! == 0) { _finalDropPosition = _itemOffsetAt(0) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(_itemExtentAt(_insertIndex! - 1), _scrollDirection); } } }); } ``` When returning from below, the code falls through to the final else block, which incorrectly adds `_extentOffset`. ## Fix Detect when `_insertIndex - item.index == 1` (indicating a return to original position from below) and animate to the correct position. ```dart if (_insertIndex! - item.index == 1) { // Drop at the original position when item returns from below _finalDropPosition = _itemOffsetAt(_insertIndex! - 1); } ``` This fix was proposed by @frankpape in flutter#90856 (comment); I've merely validated and researched the background of why the fix works, and supported it with tests. **_Demo of the fixed implementation:_** <p align="center"> <img src="https://github.com/user-attachments/assets/a53e8920-ebca-4326-abe9-3b43b34419e5" alt="fixed" width="250"> </p> Fixes flutter#88331 Fixes flutter#90856 Fixes flutter#150843 ## A note about a previous PR: While investigating this issue, I found a PR addressing what seemed to be [the same exact issue](flutter#150843): PR flutter#151026; it turns out that that PR solved a _portion_ of the edge case: the case where an item is dragged down and back and slightly **overshoots** its original position when being dragged back & dropped—but that PR did not account for the presence of this bug when the dragged item slightly **undershoots** its original position on the return drag. This new PR effectively addresses the 'undershooting' case. With this, I've added a new pair of regression tests that are identical to the [previous PR's tests](https://github.com/flutter/flutter/blob/master/packages/flutter/test/widgets/reorderable_list_test.dart#L734), except for the fact that they simulate an undershoot on the return trip (90% of the way back instead of 110% like the original tests). This definitively captures the issue, failing in the master branch and passing in this PR's branch. Here is the specific case resolved by the [**old** PR](flutter#151026): <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/b0ddc745-6e9e-4f12-97da-454e2e76b06d" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/03e181fa-f43b-4405-b0c0-16d3465ad990" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Here is the additional case resolved by **this** PR: <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/9b4bb591-aa2f-4cf0-88b8-a3ec32b0f0ac" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/31646e9c-78f4-4252-921f-53583193868f" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Two final observations worth noting: - The fix proposed in this PR seems to **supersede** the previous PR's solution; it addresses both cases (overshooting and undershooting) even in my tests with the [original PR's changes ](https://github.com/flutter/flutter/pull/151026/files#diff-23a4bb073009d89f09084bdf5f85232de135b8f11be625e6312bb85900a90e67) reverted. Probably best to keep the old PR's code anyway to be conservative, but noteworthy. - I also found it notable that neither this PR nor the older PR fix any issue with "reversed lists", which, in my tests, are simply not subject to this edge case as we've defined it. The regression tests added for the reverse case are thus purely precautionary. ## 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. <!-- 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
…172380) _**Note:** Alongside this PR, I've also prepared [another PR](flutter#172882) with an alternative solution involving a more substantial refactor that addresses the root cause, rather than adding more conditional logic._ ## Description This PR fixes the proxy animation bug where dragging a `ReorderableList` item downward and then back to its original position causes it to animate to the wrong location (one position too low). ## The Problem When dragging a `ReorderableList` item downward and then back to its original position, the proxy widget briefly animates to the wrong location (one position too low) before snapping to the correct spot. **Reproduction**: Drag any item down past at least one other item, then drag it back to where it started. <p align="center"> <img src="https://github.com/user-attachments/assets/d0931dff-5600-441c-8536-2c61789767d0" alt="demo2" width="250"> </p> ## Root Cause This bug is specific to dragging an item down and then bringing it back up to nearly (but not 100% of the way ) to its original position: 1. When the item approaches its original position **from below**, `_insertIndex` becomes `item.index + 1` - This happens because Flutter's `ReorderableList` calculates `_insertIndex` with the dragged item still present in the list (see flutter#24786) 2. The proxy _should_ animate to the item's original position at `item.index` - _But the proxy actually animates one position too low._ - This happens because `_dragEnd` incorrectly calculates `_finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(...)` - The `_extentOffset(...)` addition, designed for items dropping _between other items_, shifts the position down by one item's height - The correct calculation for "returning home from below" should be just `_itemOffsetAt(_insertIndex! - 1)` Note that this only occurs when returning from below (`_insertIndex > item.index`). Dragging upward (in a vertical list for example) or doesn't trigger this bug. ## Existing Implementation The existing `_dragEnd` method in `reorderable_list.dart`: ```dart void _dragEnd(_DragInfo item) { setState(() { if (_insertIndex == item.index) { _finalDropPosition = _itemOffsetAt(_insertIndex!); } else if (_reverse) { if (_insertIndex! >= _items.length) { _finalDropPosition = _itemOffsetAt(_items.length - 1) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex!) + _extentOffset(_itemExtentAt(_insertIndex!), _scrollDirection); } } else { if (_insertIndex! == 0) { _finalDropPosition = _itemOffsetAt(0) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(_itemExtentAt(_insertIndex! - 1), _scrollDirection); } } }); } ``` When returning from below, the code falls through to the final else block, which incorrectly adds `_extentOffset`. ## Fix Detect when `_insertIndex - item.index == 1` (indicating a return to original position from below) and animate to the correct position. ```dart if (_insertIndex! - item.index == 1) { // Drop at the original position when item returns from below _finalDropPosition = _itemOffsetAt(_insertIndex! - 1); } ``` This fix was proposed by @frankpape in flutter#90856 (comment); I've merely validated and researched the background of why the fix works, and supported it with tests. **_Demo of the fixed implementation:_** <p align="center"> <img src="https://github.com/user-attachments/assets/a53e8920-ebca-4326-abe9-3b43b34419e5" alt="fixed" width="250"> </p> Fixes flutter#88331 Fixes flutter#90856 Fixes flutter#150843 ## A note about a previous PR: While investigating this issue, I found a PR addressing what seemed to be [the same exact issue](flutter#150843): PR flutter#151026; it turns out that that PR solved a _portion_ of the edge case: the case where an item is dragged down and back and slightly **overshoots** its original position when being dragged back & dropped—but that PR did not account for the presence of this bug when the dragged item slightly **undershoots** its original position on the return drag. This new PR effectively addresses the 'undershooting' case. With this, I've added a new pair of regression tests that are identical to the [previous PR's tests](https://github.com/flutter/flutter/blob/master/packages/flutter/test/widgets/reorderable_list_test.dart#L734), except for the fact that they simulate an undershoot on the return trip (90% of the way back instead of 110% like the original tests). This definitively captures the issue, failing in the master branch and passing in this PR's branch. Here is the specific case resolved by the [**old** PR](flutter#151026): <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/b0ddc745-6e9e-4f12-97da-454e2e76b06d" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/03e181fa-f43b-4405-b0c0-16d3465ad990" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Here is the additional case resolved by **this** PR: <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/9b4bb591-aa2f-4cf0-88b8-a3ec32b0f0ac" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/31646e9c-78f4-4252-921f-53583193868f" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Two final observations worth noting: - The fix proposed in this PR seems to **supersede** the previous PR's solution; it addresses both cases (overshooting and undershooting) even in my tests with the [original PR's changes ](https://github.com/flutter/flutter/pull/151026/files#diff-23a4bb073009d89f09084bdf5f85232de135b8f11be625e6312bb85900a90e67) reverted. Probably best to keep the old PR's code anyway to be conservative, but noteworthy. - I also found it notable that neither this PR nor the older PR fix any issue with "reversed lists", which, in my tests, are simply not subject to this edge case as we've defined it. The regression tests added for the reverse case are thus purely precautionary. ## 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. <!-- 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
…172380) _**Note:** Alongside this PR, I've also prepared [another PR](flutter#172882) with an alternative solution involving a more substantial refactor that addresses the root cause, rather than adding more conditional logic._ ## Description This PR fixes the proxy animation bug where dragging a `ReorderableList` item downward and then back to its original position causes it to animate to the wrong location (one position too low). ## The Problem When dragging a `ReorderableList` item downward and then back to its original position, the proxy widget briefly animates to the wrong location (one position too low) before snapping to the correct spot. **Reproduction**: Drag any item down past at least one other item, then drag it back to where it started. <p align="center"> <img src="https://github.com/user-attachments/assets/d0931dff-5600-441c-8536-2c61789767d0" alt="demo2" width="250"> </p> ## Root Cause This bug is specific to dragging an item down and then bringing it back up to nearly (but not 100% of the way ) to its original position: 1. When the item approaches its original position **from below**, `_insertIndex` becomes `item.index + 1` - This happens because Flutter's `ReorderableList` calculates `_insertIndex` with the dragged item still present in the list (see flutter#24786) 2. The proxy _should_ animate to the item's original position at `item.index` - _But the proxy actually animates one position too low._ - This happens because `_dragEnd` incorrectly calculates `_finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(...)` - The `_extentOffset(...)` addition, designed for items dropping _between other items_, shifts the position down by one item's height - The correct calculation for "returning home from below" should be just `_itemOffsetAt(_insertIndex! - 1)` Note that this only occurs when returning from below (`_insertIndex > item.index`). Dragging upward (in a vertical list for example) or doesn't trigger this bug. ## Existing Implementation The existing `_dragEnd` method in `reorderable_list.dart`: ```dart void _dragEnd(_DragInfo item) { setState(() { if (_insertIndex == item.index) { _finalDropPosition = _itemOffsetAt(_insertIndex!); } else if (_reverse) { if (_insertIndex! >= _items.length) { _finalDropPosition = _itemOffsetAt(_items.length - 1) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex!) + _extentOffset(_itemExtentAt(_insertIndex!), _scrollDirection); } } else { if (_insertIndex! == 0) { _finalDropPosition = _itemOffsetAt(0) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(_itemExtentAt(_insertIndex! - 1), _scrollDirection); } } }); } ``` When returning from below, the code falls through to the final else block, which incorrectly adds `_extentOffset`. ## Fix Detect when `_insertIndex - item.index == 1` (indicating a return to original position from below) and animate to the correct position. ```dart if (_insertIndex! - item.index == 1) { // Drop at the original position when item returns from below _finalDropPosition = _itemOffsetAt(_insertIndex! - 1); } ``` This fix was proposed by @frankpape in flutter#90856 (comment); I've merely validated and researched the background of why the fix works, and supported it with tests. **_Demo of the fixed implementation:_** <p align="center"> <img src="https://github.com/user-attachments/assets/a53e8920-ebca-4326-abe9-3b43b34419e5" alt="fixed" width="250"> </p> Fixes flutter#88331 Fixes flutter#90856 Fixes flutter#150843 ## A note about a previous PR: While investigating this issue, I found a PR addressing what seemed to be [the same exact issue](flutter#150843): PR flutter#151026; it turns out that that PR solved a _portion_ of the edge case: the case where an item is dragged down and back and slightly **overshoots** its original position when being dragged back & dropped—but that PR did not account for the presence of this bug when the dragged item slightly **undershoots** its original position on the return drag. This new PR effectively addresses the 'undershooting' case. With this, I've added a new pair of regression tests that are identical to the [previous PR's tests](https://github.com/flutter/flutter/blob/master/packages/flutter/test/widgets/reorderable_list_test.dart#L734), except for the fact that they simulate an undershoot on the return trip (90% of the way back instead of 110% like the original tests). This definitively captures the issue, failing in the master branch and passing in this PR's branch. Here is the specific case resolved by the [**old** PR](flutter#151026): <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/b0ddc745-6e9e-4f12-97da-454e2e76b06d" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/03e181fa-f43b-4405-b0c0-16d3465ad990" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Here is the additional case resolved by **this** PR: <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/9b4bb591-aa2f-4cf0-88b8-a3ec32b0f0ac" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/31646e9c-78f4-4252-921f-53583193868f" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Two final observations worth noting: - The fix proposed in this PR seems to **supersede** the previous PR's solution; it addresses both cases (overshooting and undershooting) even in my tests with the [original PR's changes ](https://github.com/flutter/flutter/pull/151026/files#diff-23a4bb073009d89f09084bdf5f85232de135b8f11be625e6312bb85900a90e67) reverted. Probably best to keep the old PR's code anyway to be conservative, but noteworthy. - I also found it notable that neither this PR nor the older PR fix any issue with "reversed lists", which, in my tests, are simply not subject to this edge case as we've defined it. The regression tests added for the reverse case are thus purely precautionary. ## 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. <!-- 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
…172380) _**Note:** Alongside this PR, I've also prepared [another PR](flutter#172882) with an alternative solution involving a more substantial refactor that addresses the root cause, rather than adding more conditional logic._ ## Description This PR fixes the proxy animation bug where dragging a `ReorderableList` item downward and then back to its original position causes it to animate to the wrong location (one position too low). ## The Problem When dragging a `ReorderableList` item downward and then back to its original position, the proxy widget briefly animates to the wrong location (one position too low) before snapping to the correct spot. **Reproduction**: Drag any item down past at least one other item, then drag it back to where it started. <p align="center"> <img src="https://github.com/user-attachments/assets/d0931dff-5600-441c-8536-2c61789767d0" alt="demo2" width="250"> </p> ## Root Cause This bug is specific to dragging an item down and then bringing it back up to nearly (but not 100% of the way ) to its original position: 1. When the item approaches its original position **from below**, `_insertIndex` becomes `item.index + 1` - This happens because Flutter's `ReorderableList` calculates `_insertIndex` with the dragged item still present in the list (see flutter#24786) 2. The proxy _should_ animate to the item's original position at `item.index` - _But the proxy actually animates one position too low._ - This happens because `_dragEnd` incorrectly calculates `_finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(...)` - The `_extentOffset(...)` addition, designed for items dropping _between other items_, shifts the position down by one item's height - The correct calculation for "returning home from below" should be just `_itemOffsetAt(_insertIndex! - 1)` Note that this only occurs when returning from below (`_insertIndex > item.index`). Dragging upward (in a vertical list for example) or doesn't trigger this bug. ## Existing Implementation The existing `_dragEnd` method in `reorderable_list.dart`: ```dart void _dragEnd(_DragInfo item) { setState(() { if (_insertIndex == item.index) { _finalDropPosition = _itemOffsetAt(_insertIndex!); } else if (_reverse) { if (_insertIndex! >= _items.length) { _finalDropPosition = _itemOffsetAt(_items.length - 1) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex!) + _extentOffset(_itemExtentAt(_insertIndex!), _scrollDirection); } } else { if (_insertIndex! == 0) { _finalDropPosition = _itemOffsetAt(0) - _extentOffset(item.itemExtent, _scrollDirection); } else { _finalDropPosition = _itemOffsetAt(_insertIndex! - 1) + _extentOffset(_itemExtentAt(_insertIndex! - 1), _scrollDirection); } } }); } ``` When returning from below, the code falls through to the final else block, which incorrectly adds `_extentOffset`. ## Fix Detect when `_insertIndex - item.index == 1` (indicating a return to original position from below) and animate to the correct position. ```dart if (_insertIndex! - item.index == 1) { // Drop at the original position when item returns from below _finalDropPosition = _itemOffsetAt(_insertIndex! - 1); } ``` This fix was proposed by @frankpape in flutter#90856 (comment); I've merely validated and researched the background of why the fix works, and supported it with tests. **_Demo of the fixed implementation:_** <p align="center"> <img src="https://github.com/user-attachments/assets/a53e8920-ebca-4326-abe9-3b43b34419e5" alt="fixed" width="250"> </p> Fixes flutter#88331 Fixes flutter#90856 Fixes flutter#150843 ## A note about a previous PR: While investigating this issue, I found a PR addressing what seemed to be [the same exact issue](flutter#150843): PR flutter#151026; it turns out that that PR solved a _portion_ of the edge case: the case where an item is dragged down and back and slightly **overshoots** its original position when being dragged back & dropped—but that PR did not account for the presence of this bug when the dragged item slightly **undershoots** its original position on the return drag. This new PR effectively addresses the 'undershooting' case. With this, I've added a new pair of regression tests that are identical to the [previous PR's tests](https://github.com/flutter/flutter/blob/master/packages/flutter/test/widgets/reorderable_list_test.dart#L734), except for the fact that they simulate an undershoot on the return trip (90% of the way back instead of 110% like the original tests). This definitively captures the issue, failing in the master branch and passing in this PR's branch. Here is the specific case resolved by the [**old** PR](flutter#151026): <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/b0ddc745-6e9e-4f12-97da-454e2e76b06d" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/03e181fa-f43b-4405-b0c0-16d3465ad990" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Here is the additional case resolved by **this** PR: <table> <tr> <td align="center"> <img src="https://github.com/user-attachments/assets/9b4bb591-aa2f-4cf0-88b8-a3ec32b0f0ac" alt="Before" width="200"><br> <sub>Before</sub> </td> <td align="center"> <img src="https://github.com/user-attachments/assets/31646e9c-78f4-4252-921f-53583193868f" alt="After" width="200"><br> <sub>After</sub> </td> </tr> </table> Two final observations worth noting: - The fix proposed in this PR seems to **supersede** the previous PR's solution; it addresses both cases (overshooting and undershooting) even in my tests with the [original PR's changes ](https://github.com/flutter/flutter/pull/151026/files#diff-23a4bb073009d89f09084bdf5f85232de135b8f11be625e6312bb85900a90e67) reverted. Probably best to keep the old PR's code anyway to be conservative, but noteworthy. - I also found it notable that neither this PR nor the older PR fix any issue with "reversed lists", which, in my tests, are simply not subject to this edge case as we've defined it. The regression tests added for the reverse case are thus purely precautionary. ## 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. <!-- 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
Fix: Flicker when no update in index of dragged item
Resolves #150843
This PR ensures that even if we move dragged item anywhere in list and comeback to initial position, we smoothly adjust to that position.
Pre-launch Checklist
///).