Skip to content

Conversation

@HansMuller
Copy link
Contributor

@HansMuller HansMuller commented May 22, 2023

The scroll notification events reported for a press-drag-release gesture within a scrollable on a touch screen device begin with a ScrollStartNotification, followed by a series of ScrollUpdateNotifications, and conclude with a ScrollEndNotification. This protocol can be used to defer work until an interactive scroll gesture ends. For example, you might defer updating a scrollable's contents via network requests until the scroll has ended, or you might want to automatically auto-scroll at that time.

In the example that follows the CustomScrollView automatically scrolls so that the last partially visible fixed-height item is completely visible when the scroll gesture ends. Many iOS applications do this kind of thing. It only makes sense to auto-scroll when the user isn't actively dragging the scrollable around.

It's easy enough to do this by reacting to a ScrollEndNotifcation by auto-scrolling to align the last fixed-height list item (source code).

Screen.Recording.2024-02-05.at.4.30.46.PM.mov

Dragging the scrollbar thumb in a desktop application is a similar user gesture. Currently it's not possible to defer work or auto-scroll (or whatever) while the scrollable is actively being dragged via the scrollbar thumb because each scrollbar thumb motion is mapped to a scroll start - scroll update - scroll end series of notifications. On a desktop platform, the same code behaves quite differently when the scrollbar thumb is dragged.

Screen.Recording.2024-02-05.at.9.45.41.AM.mov

The stream of scroll-end events triggers auto-scrolling every time the thumb moves. From the user's perspective this feels like a losing struggle.

One can also detect the beginning and end of a touch-drag by listening to the value of a ScrollPosition's isScrollingNotifier. This approach suffers from a similar problem: during a scrollbar thumb-drag, the isScrollingNotifier value isn't updated at all.

This PR adds a ScrollPosition.dragTo() method to ScrollPosition and ScrollBar uses it, so that desktop applications can choose to respond to scrollbar thumb drags in the same that they respond to drag scrolling on a touch screen. With the changes included here, the desktop or web version of the app works as expected, whether you're listing to scroll notifications or the scroll position's isScrollingNotifier.

Screen.Recording.2024-02-02.at.4.04.51.PM.mov

This PR also makes the second ScrollPosition API doc example work as expected when used with the DartPad that's part of API doc page.

Desktop applications also see scroll start-update-end notifications due to the mouse wheel. There is no touch screen analog for the mouse wheel, so an application that wanted to enable this kind of auto-scrolling alignment would have to include a heuristic that dealt with the sequence of small scrolls triggered by the mouse wheel. Here's an example of that: source code. This version of the app does not auto-align in response to small changes, wether they're triggered by dragging the scrollbar thumb of the mouse wheel.

Related sliver utility PRs: #143538, #143196, #143325.

@flutter-dashboard flutter-dashboard bot added a: text input Entering text in a text field or keyboard related problems f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. labels May 22, 2023
@flutter-dashboard
Copy link

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 (don't just cc him here, he won't see it! He's on 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.

@HansMuller HansMuller force-pushed the scrollbar_thumb_scroll branch from ce35ce0 to 48643c3 Compare May 22, 2023 20:58
Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

Why add a method to scroll position? That will definitely fail some tests somewhere because we have customers that extend and implement scroll position.

Drag is implemented internally from scroll position in conjunction with Scrollable, so what does it mean to dragTo in the context of the actual drag implementation?

Also, it does not look like it makes sense to add that method on scroll position if in fact the scrollbar is going to manually dispatch notifications. I think sending out a start and end notification is a bad idea, because the scrollbar itself is listening to those notifications. So the scrollbar will react to the notifications it is sending, as well as natural notifications from the scroll position.

Why not just modify the isScrollingNotifier? I thought that was what was needed here and was the basis of the recommended fix. It is not actually scrolling, so I think keeping this minimal for the specific use case is important.

@HansMuller
Copy link
Contributor Author

HansMuller commented May 22, 2023

Why add a method to scroll position? That will definitely fail some tests somewhere because we have customers that extend and implement scroll position.

Developers with subclasses of ScrollPosition that override jumpTo, expecting it to be called when the scrollbar thumb is dragged, will break. I haven't found an example of that yet, but I'd be surprised even there isn't one somewhere.

FTR this PR doesn't break any existing tests, including internal tests (see Google internal link cl/534230932).

I added the ScrollPosition.dragTo() method to avoid unconditionally firing start/end scroll notifications when the scrollbar thumb is dragged to update the scroll position. The code used to call ScrollPosition.jumpTo(), which unconditionally produces a start/update/end notification each time the thumb moved.

The resulting notifications - a start when the thumb-drag starts, updates as the thumb is dragged, and an end when the thumb drag ends - match what happens when the scroll position is driven by a touch-drag gesture.

Drag is implemented internally from scroll position in conjunction with Scrollable, so what does it mean to dragTo in the context of the actual drag implementation?

I'm not quite sure what you're getting at. ScrollPosition.dragTo is the same as ScrollPosition.jumpTo without the activity transitions and extra notifications. Dragging the scrollbar's thumb used to call jumpTo, now it calls dragTo. We could call it updateTo instead of dragTo. I suppose we could even redefine jumpTo():

  @override
  void jumpTo(double value) {
    goIdle();
    if (pixels != value) {
      didStartScroll();
      updateTo(value);
      didEndScroll();
    }
    goBallistic(0.0);
  }

Also, it does not look like it makes sense to add that method on scroll position if in fact the scrollbar is going to manually dispatch notifications. I think sending out a start and end notification is a bad idea, because the scrollbar itself is listening to those notifications. So the scrollbar will react to the notifications it is sending, as well as natural notifications from the scroll position.

There may be a better way to send the start and end scroll notifications then having the scrollbar do so directly. On the other hand, only the scrollbar (thumb) knows when the drag gesture ends.

Why not just modify the isScrollingNotifier? I thought that was what was needed here and was the basis of the recommended fix. It is not actually scrolling, so I think keeping this minimal for the specific use case is important.

If scrollbar just modifies isScrollingNotifier's value, the notification listeners see a series of start/update/end notifications, albeit with isScrolling always true. That doesn't match what happens during a touch-drag gesture.

Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

Developers with subclasses of ScrollPosition that override jumpTo, expecting it to be called when the scrollbar thumb is dragged, will break

I am less concerned about this, and more concerned about developers that implement ScrollPosition and will be broken having not implemented this method. I am still not clear why it is necessary, or as a developer what it means to implement dragTo when drag is already implemented differently. Same for updateTo - what does this mean semantically in terms of scroll position? Or is this just for use only by the scrollbar?

match what happens when the scroll position is driven by a touch-drag gesture.

I am still not convinced that the scrollbar should be expected to do this. The scrollable widget is not actually being dragged. The scrollbar widget is.

I'm not quite sure what you're getting at.

I don't think we should add a method to scroll position if we pursue the scrollbar producing any kind of scroll notification. If you want to modify the position without triggering notifications, methods already exist, like ScrollPosition.correctPixels.

I suppose we could even redefine jumpTo()

That would likely be a large breaking change.

I understand the motivation of this change to be for a custom sliver that needs to know about the position changing. This should already be possible with the information provided to slivers. They know when the position is changing. I would be happy to take a look at your custom sliver and see if there is a better way to achieve the desired affect.

I feel like having the scrollbar dispatch scroll notifications breaks with Flutter's agnostic widget design. ScrollNotifications come from a scrollable widget. I don't know that we should add more complexity here as described in https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#avoid-interleaving-multiple-concepts-together

@Hixie
Copy link
Contributor

Hixie commented Aug 1, 2023

Spoke with @HansMuller and he said this is back on his radar, so leaving open.

@Hixie
Copy link
Contributor

Hixie commented Oct 31, 2023

Spoke with @HansMuller and he said this is still on his radar, so leaving open.

1 similar comment
@goderbauer
Copy link
Member

Spoke with @HansMuller and he said this is still on his radar, so leaving open.

@flutter-dashboard
Copy link

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.

@HansMuller HansMuller force-pushed the scrollbar_thumb_scroll branch from 48643c3 to 2ff0940 Compare February 6, 2024 20:51
@github-actions github-actions bot added d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos labels Feb 6, 2024
@HansMuller HansMuller force-pushed the scrollbar_thumb_scroll branch 2 times, most recently from c8e54d4 to 82308e4 Compare February 8, 2024 01:23
@HansMuller HansMuller changed the title [DRAFT] - Added ScrollPosition.dragTo() Added ScrollPosition.dragTo() Feb 8, 2024
@HansMuller HansMuller force-pushed the scrollbar_thumb_scroll branch 3 times, most recently from 5fd9999 to a69796f Compare February 13, 2024 23:16
Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

I still do not think this is the right approach, for all the reasons discussed before and mentioned here now. See #78688 for more supporting evidence that the scrollbar should use the existing drag API.

I do not understand adding a new public dragTo method that just calls jumpTo when it already has drag.

Comment on lines +936 to +938
void dragTo(double value) {
jumpTo(value);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This really feels like a workaround. It is public API we are committing to in this change that does not make sense for what it is doing.

ScrollPosition already has a drag API. The scrollbar should use that. That feels like the correct way to resolve the issue here and would also resolve #78688

Comment on lines +1630 to +1632
position.dragTo(newPosition);
} else {
position.jumpTo(newPosition);
Copy link
Contributor

Choose a reason for hiding this comment

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

If dragTo is just calling jumpTo, what is the difference?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

dragTo captures a specific use gesture. It gives the scrollbar a way to distinguish a thumb-drag, which is something that the user decided to do, vs a programmatic scroll, which might not be. The idea was to give the app a way to change it's behavior only while the thumb is being dragged, rather than any time the app jumped the scroll offset around.

Copy link
Contributor

Choose a reason for hiding this comment

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

But it is just calling the same method jumpTo behind dragTo, what makes it different?

@HansMuller
Copy link
Contributor Author

Closing this PR in favor of #146654.

@HansMuller HansMuller closed this Apr 11, 2024
HansMuller added a commit to HansMuller/flutter that referenced this pull request Jun 3, 2024
auto-submit bot pushed a commit that referenced this pull request Jun 6, 2024
A sliver that is pinned to the start of its `CustomScrollView` and reacts to scrolling by resizing between the intrinsic sizes of its min and max extent prototypes.

The minimum and maximum sizes of this sliver are defined by `minExtentPrototype` and `maxExtentPrototype`, a pair of widgets that are laid out once. You can use `SizedBox` widgets to define the size limits.

This sliver is preferable to the general purpose `SliverPersistentHeader` for its relatively narrow use case because there's no need to create a `SliverPersistentHeaderDelegate` or to predict the header's minimum or maximum size.

The sample shows how this sliver's two extent prototype properties can be used to create a resizing header whose minimum and maximum sizes match small and large configurations of the same header widget.

https://github.com/flutter/flutter/assets/1377460/fa7ced98-9d92-4d13-b093-50392118c213

Related sliver utility PRs: #143538, #143196, #127340.
auto-submit bot pushed a commit that referenced this pull request Jun 7, 2024
A sliver that remains �pinned� to the top of the scroll view. Subsequent slivers scroll behind it. Typically the sliver is created as the first item in the list however it can be inserted anywhere and it will always stop at the top of the scroll view. When the scrolling axis is vertical, the PinnedHeaderSliver�s height is defined by its widget child. Multiple PinnedHeaderSlivers will layout one after the other, once they've scrolled to the top.

This sliver is preferable to the general purpose SliverPersistentHeader - for its relatively narrow use case - because there's no need to create a [SliverPersistentHeaderDelegate] or to predict the header's size.

Here's a [working demo in DartPad](https://dartpad.dev/?id=3b3f24c14fa201f752407a21ca9c9456).

https://github.com/flutter/flutter/assets/1377460/943f2e02-8e73-48b7-90be-61168978ff71

Related sliver utility PRs: #143538, #143325, #127340.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants