Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add snapAnimationCurve to DraggableScrollableSheet #166134

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Kal-Elx
Copy link

@Kal-Elx Kal-Elx commented Mar 28, 2025

This pull request adds an optional parameter snapAnimationCurve to DraggableScrollableSheet which controls the curve of the snapping animation when the sheet is released after drag.

This fixes #121996 where the current snap animation of DraggableScrollableSheet is described as linear/mechanic.

The reason why it feels mechanic is because the animation is calculated position + velocity * time.
This pull request changes this calculation to instead calculate the position as position + displacement * snapAnimationCurve!.transform(progress).

Here's an example of the result with snapAnimationCurve: Curves.easeOutQuad:
https://github.com/user-attachments/assets/b54a5821-f2cf-4280-b30d-8138082da3c8

And another with

snapAnimationCurve: Curves.bounceOut,
snapAnimationDuration: const Duration(milliseconds: 1200),
Simulator.Screen.Recording.-.iPhone.16.-.2025-03-28.at.14.34.39.mp4

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

Discussion

The provided solution does not fully support curves that can take values > 1 or < 0, e.g. Curves.easeOutBack. Using one of these curves could try to move the sheet outside of its minChildSize and maxChildSize which the current implementation does not support so the sheet would just halt at its min and max positions. I think this should be addressed but I would argue that it's outside of the scope of this PR since it's also an existing problem when using animateTo on DraggableScrollableController and any solution in this area would likely solve both problems.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: scrolling Viewports, list views, slivers, etc. labels Mar 28, 2025
Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this!

@gnprice I wonder if you still have thoughts about this since you commented on #121996 awhile back. Maybe as a first step we should just match the native Android curve by default?

The Material spec doesn't seem to say what the curve should be. I only see this predictive back gif, which happens to show the snap animation too. It's definitely not linear. https://m3.material.io/components/bottom-sheets/guidelines

Adds an optional parameter snapAnimationCurve to DraggableScrollableSheet that
controls the behaviour of the snap animation after the sheet is dragged and then
released.
@Kal-Elx Kal-Elx force-pushed the add_snap_animation_curve_to_draggable_scrollable_sheet branch from 4a95375 to ed40580 Compare April 16, 2025 10:17
@Kal-Elx
Copy link
Author

Kal-Elx commented Apr 16, 2025

Thanks for taking the time to look at this @justinmc!

I added a test that verifies that the sheet follows the given snapAnimationCurve during snapping.

Let me know if I can do anything else.

@flutter-dashboard
Copy link

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #166134 at sha ed40580

@flutter-dashboard flutter-dashboard bot added the will affect goldens Changes to golden files label Apr 16, 2025
Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@@ -305,6 +305,7 @@ class DraggableScrollableSheet extends StatefulWidget {
this.snap = false,
this.snapSizes,
this.snapAnimationDuration,
this.snapAnimationCurve = Curves.linear,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we change this default value to something other than linear? (Maybe in a separate PR)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you guys. I'd probably do it in a separate PR but let me know if you want me to change it here :)

@justinmc justinmc requested a review from gnprice April 18, 2025 18:50
@Kal-Elx
Copy link
Author

Kal-Elx commented Apr 20, 2025

Thanks for the review @justinmc! I've fixed the comment. Let me know if I should do something more to get this merged.

@justinmc
Copy link
Contributor

Let's wait for a secondary review from @gnprice who I think has been OOO.

@gnprice
Copy link
Member

gnprice commented Apr 30, 2025

Yeah, I still feel like adding this API doesn't solve most of the problem — a much bigger improvement would be if we change the default from Curves.linear to whatever is our best estimate of what the curve should be. I think everyone's agreed that Curves.linear isn't the best default.

@Kal-Elx What curve are you planning to use for this argument in your app or apps? How did you choose that curve? Potentially that curve should be the default.

@gnprice
Copy link
Member

gnprice commented Apr 30, 2025

The Material spec doesn't seem to say what the curve should be. I only see this predictive back gif, which happens to show the snap animation too. It's definitely not linear. https://m3.material.io/components/bottom-sheets/guidelines

Cool, good find. (Down at the bottom of that page.)

One thing I notice there is that it seems like there are two different curves involved:

  • When the sheet is entering, it uses a curve that starts quickly and ends slowly (something like Curves.easeOut).
  • When the sheet is exiting, it uses a curve that starts slowly and ends quickly (something like Curves.easeIn).

Would both of those be controlled by the curve parameter here? (I'm not sure if they would — I don't have my head around all the interactions involved in DraggableScrollableSheet.) If so, then it seems like the API in this PR wouldn't yet be enough to enable an app to match the Material behavior.

@gnprice
Copy link
Member

gnprice commented Apr 30, 2025

I just went and experimented with Google Maps on an Android phone, since that's an example of an app that uses a bottom sheet with several snap sizes and that generally seems to have a good Material implementation.

Dragging and flinging it between the snap sizes, it's definitely using some sort of ease-out curve (one that starts quickly and ends slowly).

I'm also realizing now that the gif we discussed above may not be quite on point: it shows the sheet entering, and exiting, but not moving between two snap sizes where both the start and end size have it still existing. I think the latter interaction is what this code is most about, and I'm not sure this code covers either the entrance or exit animations.

@gnprice
Copy link
Member

gnprice commented Apr 30, 2025

Here's a screen recording of the Google Maps behavior:
https://github.com/user-attachments/assets/eb2d854f-2734-4cd9-af6e-e6250170d23f

@gnprice
Copy link
Member

gnprice commented Apr 30, 2025

Dragging and flinging it between the snap sizes, it's definitely using some sort of ease-out curve (one that starts quickly and ends slowly).

More precisely, as I look more closely: if you drag the sheet to an intermediate point but then let go without flinging (i.e. with your finger not moving, or at least not moving quickly), then it (a) starts from a standstill, just like it was when you let go, but (b) accelerates quickly early on, and then (c) slows down more gradually to a stop. Roughly like Curves.ease.

There's an example of this around 00:13 in my screen recording above.

I think the behavior here can't be captured by a single curve (or a single Curve, anyway), because of the way it depends on the initial velocity of the fling:

  • If you let go from a standstill, then it first accelerates and then decelerates, as I described just now.
  • If you let go while moving quickly, then it starts at that rapid speed and decelerates from there.

The Simulation concept is well suited for this sort of thing, though.

Comment on lines +1156 to +1159
final double averageVelocity =
_pixelSnapSize < position
? math.min(-minimumSpeed, initialVelocity)
: math.max(minimumSpeed, initialVelocity);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value of initialVelocity comes from the user's fling, right? I.e. it's the velocity their finger was moving at when they let go.

With this formula, the actual initial velocity that the snapping simulation moves at will be different from initialVelocity. For example, if the curve is one that starts quickly and ends slowly, then the sheet will suddenly speed up at the instant the user lets go, and then gradually slow down.

That seems likely to feel unnatural. The existing code already does that if you start from a stop, or from a low speed, thanks to the minimumSpeed logic here… and I think that's actually one of the major reasons that the current behavior doesn't feel right. See my observations above (#166134 (comment)) about how Google Maps behaves: if you start from low speed then it does accelerate, but smoothly rather than all in one frame.

Instead I think the ideal behavior here would have the simulation's initial velocity, i.e. dx(0.0), be equal to initialVelocity. Then it can speed up and/or slow down smoothly from there.

@Kal-Elx
Copy link
Author

Kal-Elx commented May 23, 2025

Hey @gnprice — sorry for the late response and thanks for taking the time to compare snapping behaviors!

In the app I’m working on right now we ended up using Curves.easeOut and it's a major improvement.

You’re absolutely right that inserting a curve means the simulation’s actual initial velocity no longer equals the user’s fling velocity but that mismatch already exists in the current api if you set snapAnimationDuration since then the sheet animates for a fixed time regardless of how hard the user flings. In my opinion allowing a curve doesn’t worsen the status quo; it just lets us make the motion feel better within the same constraint.

I definitely agree this isn’t the ideal long-term solution, and I’d love to see an implementation that truly matches the Material behavior when there's time to implement it. In the meantime, merging this feels like a practical win for developers without introducing new risk.

Of course the final call is yours and the framework team’s. Thanks again for the thoughtful review! 🙏

@Piinks
Copy link
Contributor

Piinks commented Jun 18, 2025

Following up on where we left off here, @gnprice did you see @Kal-Elx's response to your feedback?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. will affect goldens Changes to golden files
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add way to change DraggableScrollableSheet snap animation curve
4 participants