Repository navigation
Add Snapping Behavior to DraggableScrollableSheet - #84394
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 Hixie on the #hackers channel in Chat. 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. |
|
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed (or fixed any issues), please reply here with What to do if you already signed the CLAIndividual signers
Corporate signers
ℹ️ Googlers: Go here for more info. |
caseycrogers
left a comment
There was a problem hiding this comment.
Open questions/comments that I'd love to get feedback on from someone with more Flutter experience than me!
|
@googlebot I signed it! |
|
@dnfield Hello Dan! This PR closes out #34111. I noticed you've previously contributed to |
|
Just a comment about the behavior @caseycrogers, using |
Yeah this is a good point. I've been pretty distracted lately but will get back to this in a bit. When I do I'll look for a way to snap immediately when the user's finger is released. |
Responded in the issue! |
|
@dnfield let me know if you want any more input from me before reviewing! |
|
Gold has detected about 1 new digest(s) on patchset 54. |
caseycrogers
left a comment
There was a problem hiding this comment.
@dnfield Resolved formatting comments. I had two questions (see open conversations), once I get more feedback I'll push a commit and resolve them.
|
Hi I have tested your PR and would prefer to have a control how the snapping animation runs. (setting curves etc.) |
| @@ -298,12 +353,38 @@ class _DraggableScrollableSheetState extends State<DraggableScrollableSheet> { | |||
| _extent = _DraggableSheetExtent( | |||
There was a problem hiding this comment.
@dnfield It's just occurred to me that this code in prod and in my branch is a bit broken:
DraggableScrollableSheet does not update the extent when the widget is rebuilt with new values. This means that the extent will retain potentially invalid values for min, max, snapSizes, etc. if the user rebuilds the sheet with new parameters.
We need to override DidUpdateWidget to rebuild the extent when necessary. This feels fairly non-trivial and is largely about fixing existing behavior in prod-are you okay with me leaving this fix to a follow up PR to go in after this one merges? Or should I spin up a new PR now to fix it and rebase this one on top of it? Seems like a fairly severe bug that may justify the latter.
There was a problem hiding this comment.
Here's a (failing in prod) test that can be added to draggable_scrollable_sheet_test.dart to demonstrate the issue:
testWidgets('A changed maxChildSize is propagated', (WidgetTester tester) async {
const Key stackKey = ValueKey<String>('stack');
const Key containerKey = ValueKey<String>('container');
await tester.pumpWidget(_boilerplate(null,
snap: true,
stackKey: stackKey,
containerKey: containerKey,
snapSizes: <double>[.5],
));
await tester.pumpAndSettle();
final double screenHeight = tester.getSize(find.byKey(stackKey)).height;
await tester.drag(find.text('Item 1'), Offset(0, -.4 * screenHeight));
await tester.pumpAndSettle();
expect(
tester.getSize(find.byKey(containerKey)).height / screenHeight,
closeTo(1.0, precisionErrorTolerance),
);
// Pump the same widget but with a new snapSizes, triggering a rebuild.
await tester.pumpWidget(_boilerplate(null,
snap: true,
stackKey: stackKey,
containerKey: containerKey,
maxChildSize: .8,
snapSizes: <double>[.5],
));
await tester.pumpAndSettle();
// The max child size has been reduced, we should be rebuilt at the new
// max of .8.
expect(
tester.getSize(find.byKey(containerKey)).height / screenHeight,
closeTo(.8, precisionErrorTolerance),
);
}, variant: TargetPlatformVariant.all());There was a problem hiding this comment.
Doing this in a new PR is fine. https://github.com/flutter/flutter/pull/69724/files probbly would fix this, and relanding that without the bit about caching the child is probably a good idea.
| for (int index = 0; index < (widget.snapSizes?.length ?? 0); index += 1) { | ||
| final double snapSize = widget.snapSizes![index]; | ||
| assert(snapSize >= widget.minChildSize && snapSize <= widget.maxChildSize, | ||
| 'Snap sizes must be between `minChildSize` and `maxChildSize`. Invalid size: $snapSize'); |
There was a problem hiding this comment.
nit: here and below, probably good to dump the whole list and point to the invalid one rather than just printing the invalid one, which might occur multiple times in a badly formed list.
There was a problem hiding this comment.
Fix'd!
Invalid snapSize '0.4' at index 3 of:
[0.3, 0.4, 0.5, >>> 0.4 <<<, 0.6, 0.7]
Snap sizes must be in ascending order.
However, I'm getting the canned Flutter message "Either the assertion indicates an error in the framework itself, or we should..." message. It looks like it's just naively checking if the top two frames of the stack are Flutter and not user code? Do you know any ways to get rid of this? I guess putting the assert in the constructor would solve this but then (as discussed earlier) we lose const and that's a breaking change.
Maybe losing const is worth moving the asserts up? Potentially a breaking-change follow up PR?
There was a problem hiding this comment.
Feel free to file a bug for that - that code has a history of breaking :\
There was a problem hiding this comment.
File a bug for moving the asserts into the constructor, or file a bug for the "Either the assertion..." message firing?
There was a problem hiding this comment.
For the Either the assertion message :)
dnfield
left a comment
There was a problem hiding this comment.
LGTM with nitpick.
Thanks for sticking with this.
If you plan to support supplying custom curves for the animation, maybe we can do that in a separate PR?
NP, thanks for all the feedback! Happy to do that in a separate PR. I fixed the nit, but there's still one minor issue with the assert message (responded in comment thread). |
|
@dnfield How do we get this merged? It looks like the tests are permanently pending? |
|
Hm. Can you push up an empty commit to kick CI? Something seems to have gotten hung up. |
|
Things appear very busted :/. I tried Edit: looks like my upstream was configured improperly so |
|
Looks like ci is running now |
|
It looks like the git issue here may have broken the flutter-gold check. I will watch this and merge manually once the Google testing shard passes, hoping this will unblock the flutter-gold check. |
|
The google check also appears broken here, I am going to merge this manually. I confirmed the google testing shard passed. |
|
Thanks a lot for this! |
|
This PR is so important to our project and I can't wait for a stable release. Because of so, I took the file This is how I did that:
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
import 'package:your_project/src/third_party/draggable_scrollable_sheet_PR_84394/draggable_scrollable_sheet.dart';
import 'package:flutter/material.dart';
import 'package:your_project/src/third_party/draggable_scrollable_sheet_PR_84394/draggable_scrollable_sheet.dart' as draggable_scrollable_sheet_mod;This is a bit hacky but manageable with proper README explaining what's happening. Sometimes I would rather use first-party packages than third-party. No regrets! Kudos to the PR author, Googlers, and community that helped push this. |
|
@om-ha Also note (and for others reading this) that the snapping functionality is already out on the beta channel if you'd rather use it that way! See switching channels. |
This PR adds two new optional arguments to
DraggableScrollableSheetcalledsnapandsnapSizes. Ifsnapis false (default value), the widget's behavior remains unchanged. Ifsnapis true, the widget will snap tosnapSizeswhen the user's finger leaves the screen during a drag. See for details: #34111 (comment).This is achieved by replacing (if a snap should occur) the sheet's ballistic simulation in
_DraggableScrollableSheetExtent.goBallisticwith one that snaps to targets instead of moving forward until velocity is dissipated.Fixes: #34111
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.