Fixing crash caused by Scrollable.animateTo interrupting HoldActivity before proper disposal is possible#37267
Fixing crash caused by Scrollable.animateTo interrupting HoldActivity before proper disposal is possible#37267Piinks wants to merge 10 commits intoflutter:masterfrom
Conversation
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
Outdated
Show resolved
Hide resolved
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
Outdated
Show resolved
Hide resolved
|
I just tried to reproduce this with the code given in #14452 and I am seeing a slightly different chain of events to get to the crash: It doesn't seem to be the user gesture that's interrupted by the automated animation that's causing the crash. Instead, it seem to be the other way around: The animation is interrupting an "in-progress" user gesture and that causes the crash. (Maybe that's what was meant by the PR description and I misunderstood?) Here is the sequence of events I am seeing:
Is this another sequence that leads to a crash? Or is that what you meant? |
|
Also, I am not convinced that this actually fixes the root cause of the crash. It removes a notification, that is used in user-defined code to trigger the animation. The animation could also be triggered via other means from user code (e.g. a timer that just happens to fire in the right moment) and that would still crash... |
No, the timer will not cause the crash, it has to happen at the exact moment at this line. After calling position.hold(_disposeHold) before returning the hold handle. This is a sync function except it trigger a a notification in the middle which allow other action to interrupt before the hold handle returns. I think the other way to fix this is not letting automatic action interrupt user action, but we still want to fix hold action trigger an errant notification. That is why I think this is the right fix since i cannot think of any other way automatic action can interrupt user action if we fix the errant notification. |
Yes that is what i observed as well. |
Yes. I also agree. Very well explained. :) I'm going to see about addressing the notification error, since I think we all agree that a HoldActivity does not constitute that the ScrollDirection is idle. As for other cases where an animation may interrupt a user gesture, I will see about creating a reproduction of the timer case you recommended @goderbauer and see if it causes a crash. |
|
I created a gist that triggers the animation after a time delay instead of after a notification. Available here: https://gist.github.com/Piinks/955c449d2764e4591b574859c7a0395f If you hold (don't drag) after triggering the future that delays the animation, the |
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
Outdated
Show resolved
Hide resolved
|
@chunhtai Ah, I see. Looking at the line you pointed out: The underlying problem is that We also dispatch a scrollendnotification when the hold starts: flutter/packages/flutter/lib/src/widgets/scroll_position.dart Lines 638 to 639 in 4ccd811 What if you trigger the animation from that one? |
Codecov Report
@@ Coverage Diff @@
## master #37267 +/- ##
==========================================
+ Coverage 55.22% 56.04% +0.82%
==========================================
Files 193 193
Lines 18172 18141 -31
==========================================
+ Hits 10036 10168 +132
+ Misses 8136 7973 -163
Continue to review full report at Codecov.
|
Yes, I think that is another corner case. it seems like we should somehow know how to reset _hold when it gets interrupted before 'hold' returns. |
Thank you for the feedback. I will look into this corner case as well and see if adding a reset _hold type method would be a better approach to fixing this crash. I will close this for now while I investigate further. |
|
That being said, I am still thinking this pr is worth fixing as hold activity should not be consider as idle according to the documentation |
|
I've added a reset-like path for when an animation is fired before the hold can be disposed of properly. A similar functionality existed in the class for keeping track of |
| delegate: this, | ||
| onHoldCanceled: holdCancelCallback, | ||
| ); | ||
| _currentHold = holdActivity; |
There was a problem hiding this comment.
Can we assert that _currentHold is null to ensure we are not overwriting a hold?
| // the [ScrollableState] has not yet received the handle to the | ||
| // [HoldActivity], it will not be properly disposed of in [beginActivity]. | ||
| // Therefore, we dispose of any active hold here. | ||
| if (_currentHold != null) { |
There was a problem hiding this comment.
Would jumpTo have the same issue?
| // the [ScrollableState] has not yet received the handle to the | ||
| // [HoldActivity], it will not be properly disposed of in [beginActivity]. | ||
| // Therefore, we dispose of any active hold here. | ||
| if (_currentHold != null) { |
There was a problem hiding this comment.
And wouldn't we also have a similar problem when there is an ongoing drag? (e.g. when an animation triggers based on the onScrollStartNotification before we had a chance to get the handle for the drag)
|
After offline chat with @goderbauer, the problem here lies in the potential gap that is created when a scroll notification is used to initiate an activity. It leaves open a small window where the activity can interrupt a currently executing one since scroll notifications are dispatched at different points in an activity's execution. |
Description
This PR fixes a crash caused by an animating
ScrollPositionWithSingleContextinterrupting an activeHoldActivitybefore the handle has been returned to theScrollable. When this happens, theHoldActivitycannot be disposed of properly. This change keeps track of the_currentHoldfrom within theScrollPositionWithSingleConextso that is may be disposed of in this case, effectively resetting the ScrollActivity.Related Issues
Fixes #14452
Fixes #11433
Tests
I added the following tests:
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?