这是indexloc提供的服务,不要输入任何密码
Skip to content

Assert for valid ScrollController in Scrollbar gestures #81278

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

Merged
merged 15 commits into from
Jun 10, 2021

Conversation

davidmartos96
Copy link
Contributor

@davidmartos96 davidmartos96 commented Apr 27, 2021

Fixes #81277

This PR includes a check for scrollbar track taps when there is no scroll position available.
The check is the same as in the other scrollbar gestures:

final Axis? direction = getScrollbarDirection();

final Axis? direction = getScrollbarDirection();

final Axis? direction = getScrollbarDirection();

I wasn't able to find similar tests for error situations with the other scrollbar gestures. This one in particular happens when we pass a scroll controller to a scroll view but not to the scrollbar. Edit: Added some tests

Kindly pinging @Piinks because these checks were added in the previous refactor of the scrollbar.dart file.

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 feature I am adding, or Hixie said the PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label Apr 27, 2021
@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.

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.

@google-cla google-cla bot added the cla: yes label Apr 27, 2021
@davidmartos96
Copy link
Contributor Author

Update:
I've added the same null check in the CupertinoScrollbar thumb press gesture from @TahaTesser comment in #81277 (comment)

This same check can also be found in the other CupertinoScrollbar gestures:

if (getScrollbarDirection() == null) {

@goderbauer goderbauer requested a review from Piinks April 28, 2021 21:48
@tvolkert
Copy link
Contributor

tvolkert commented May 2, 2021

Thanks for the contribution! Is there a test you can add that shows what issue this fixes (that would have failed before your patch)?

@davidmartos96
Copy link
Contributor Author

@tvolkert I tried looking into related tests in the framework but I didn't find any. In fact, I removed the checks from all gestures in the scrollbar and tests were still passing.
I'm not sure how to test this situation in particular because it implies a first step of quickly moving the scrollbar so that it appears before doing the gesture that crashes. We cannot use the isAlways shown property here.

@flutter-dashboard flutter-dashboard bot added the f: cupertino flutter/packages/flutter/cupertino repository label May 3, 2021
@davidmartos96
Copy link
Contributor Author

@tvolkert I've added some tests.

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.

Hi @davidmartos96 thank you for filing an issue and writing a PR for it! :)

This one in particular happens when we pass a scroll controller to a scroll view but not to the scrollbar.

This is the cause of the issue. You are seeing this error in your code because the Scrollbar is trying to use the PrimaryScrollController. It uses this ScrollController when one has not been provided. Since you have provided a ScrollController to the ListView, the Scrollbar and ListView are essentially detached. The Scrollbar still updates, since it listens to ScrollNotifications that bubble up, but when you try to interact with it, it is trying to use a different controller than the one your ListView actually has.

Providing your ListView's ScrollController to the Scrollbar should resolve your issue, or if not needed, you can remove the scroll controller from the ListView.

So, while this change prevents an exception from being thrown, it actually introduces a way for users to break their scrollbar without knowing why. I've updated the bug to reflect a need for better error messaging here, we recently added some in #77107 for isAlwaysShown, I think we could benefit from the same here too. :)

Would you like to update this PR to pursue that?

await scrollGesture.up();
await tester.pump();

// Scroll doesn't move because the scrollbar doesn't handle gestures
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of allowing a misconfiguration between user-provided scroll controller and primary scroll controller, you can disable gestures with Scrollbar.interactive. Making this do nothing may be confusing to users that otherwise would not understand why their scrollbar does not work.

@davidmartos96
Copy link
Contributor Author

davidmartos96 commented May 10, 2021

@Piinks Thanks! Yes, I've ended up figuring out the cause.
The problem here is that is not clear that the Scrollbar always needs a scroll controller to function correctly visually (without interaction).
On our 2 year old app we've been using scrollbars here and there, and when we needed a scroll controller, we only passed it to the listview and co.
Do you think the fix (the early return) is still worthy to be included? I believe it is, given the similar checks that other gestures have and because it is an optional parameter that only affects the interactivity. In many cases one only wants to see the amount scrolled, which works fine even when using different controllers.
I don't really understand how the isAlwaysShown applies here. For this use case I don't mind for it to hide. Do you mean including an assert to prevent this kind of mistake from happening?

@davidmartos96
Copy link
Contributor Author

It was only until now that we included a crash reporting tool that we found those errors on production code. We've never found those while developing all this time.

@Piinks
Copy link
Contributor

Piinks commented May 10, 2021

@Piinks Thanks! Yes, I've ended up figuring out the cause.
The problem here is that is not clear that the Scrollbar always needs a scroll controller to function correctly visually (without interaction).

Visually, no it does not need a ScrollController, we actually used to assert that a scroll controller was always provided for interaction. We removed that to allow the PrimaryScrollController to be an option and remove some of the churn of always having to wire it up yourself.

This is because we wanted to support multiple options for the Scrollbar. You do not always have to provide a scroll controller to the scrollbar, but in this case you do. There are some examples and documentation updates that have not arrived to the stable branch yet, you can see the master branch documentation here for Scrollbar, there is more under RawScrollbar and CupertinoScrollbar as well.

On our 2 year old app we've been using scrollbars here and there, and when we needed a scroll controller, we only passed it to the listview and co.

We do have a new feature that recently landed that allows you to add a Scrollbar as part of the ScrollBehavior. This might be of benefit to your use case as the ScrollBehavior handles setting everything up for you - including the ScrollController. There is a migration guide available here to show you how to do this using ScrollBehavior.buildScrollbar. This could resolve your problem another way and never require you to wire up ScrollView/Scrollbar yourself. :)

Do you think the fix (the early return) is still worthy to be included? I believe it is, given the similar checks that other gestures have and because it is an optional parameter that only affects the interactivity. In many cases one only wants to see the amount scrolled, which works fine even when using different controllers.

I do not think we want the early returns as it breaks the scrollbar without any explanation to the user.

I don't really understand how the isAlwaysShown applies here. For this use case I don't mind for it to hide. Do you mean including an assert to prevent this kind of mistake from happening?

I don't think this is related to isAlwaysShown, the PR I linked was meant to illustrate the type of error messaging
we have been adding to help users identify this issue. I think we can take a similar approach/error message for this case that informs the user what scroll controller the scrollbar is trying to use.

@davidmartos96
Copy link
Contributor Author

davidmartos96 commented May 11, 2021

@Piinks Oh, lots of new things coming to the scrollbar. Thanks for the info!

I do not think we want the early returns as it breaks the scrollbar without any explanation to the user.

I'm looking into the error message improvement. How should we go about it? Currently the getScrollbarDirection() is documented to return null when no position is attached and the gesture handlers look like this:

  void handleThumbPressStart(Offset localPosition) {
    final Axis? direction = getScrollbarDirection();
    if (direction == null) {
      return;
    }
    ....

How do you think it would be better?

  1. Throw after the check
  void handleThumbPressStart(Offset localPosition) {
    final Axis? direction = getScrollbarDirection();
    if (direction == null) {
      // Call an assertion that throws the explanation like on the isAlwaysShown situation.
      assertion();
      return;
    }
    ....
  1. Change the getScrollbarDirection API to not return null and show the appropriate error message from inside that function.

  2. Call a check method at the start of every gesture

  void handleThumbPressStart(Offset localPosition) {
    // Throws the FlutterError internally
    _checkForValidScrollPosition();
    ....
  1. Any other suggestion

We could also change the API of getScrollbarDirection to not return null and call the check at the start as in 3)

@Piinks
Copy link
Contributor

Piinks commented May 17, 2021

Something along the lines of 3 sounds good to me! Thank you for continuing to work on this and making the error messaging better! :)

You could probably take most of the error from the isAlwaysShown and put it in a method to reuse here and there.

@davidmartos96 davidmartos96 changed the title Check for attached clients in scrollbar track tap Assert for correct ScrollController in Scrollbar gestures May 17, 2021
@davidmartos96 davidmartos96 changed the title Assert for correct ScrollController in Scrollbar gestures Assert for valid ScrollController in Scrollbar gestures May 17, 2021
@davidmartos96
Copy link
Contributor Author

@Piinks I've updated the PR and tests with the suggestions

@davidmartos96
Copy link
Contributor Author

I've been thinking a bit more about this. Do you think we should have this assertion check when building the scrollbar for the first time?. In the same place as the isAlwaysShown check, but unconditionally.


Otherwise, even if the error is more explicit now, it will only be shown when using the thumb or the track gestures + an incorrect injection of the controller. That has low chances of being noticed while developing.
I think it would be better if users notice that they have something wrong in the scrollbar as early as possible. I've been using them incorrectly for over 2 years without noticing 😅
What do you think?

@Piinks
Copy link
Contributor

Piinks commented May 18, 2021

I think that sounds like a great idea! If RawScrollbar.interactive is true, we could definitely surface that error sooner for gestures. You can have a scrollbar that is not interactive, but isAlwaysShown, so I think those would be two separate checks still.

@davidmartos96
Copy link
Contributor Author

@Piinks Any update on this?

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.

Hey @davidmartos96, apologies for the delay. I've been away for a bit. Can you add tests for the changes to the Cupertino widgets you have made here?

Comment on lines +1054 to +1056
child: SingleChildScrollView(
controller: controller,
child: const SizedBox(width: 4000.0, height: 4000.0),
Copy link
Contributor

Choose a reason for hiding this comment

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

NICE

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No problem! What kind of test are you thinking of? Actually, I had to add the small extra code so that the current Cupertino tests pass. Maybe the current tests are enough?

@Piinks Piinks added f: scrolling Viewports, list views, slivers, etc. a: error message Error messages from the Flutter framework a: quality A truly polished experience labels Jun 2, 2021
@davidmartos96
Copy link
Contributor Author

I've included 2 tests that check that the controllers used internally are different because the primary one can't be attached to multiple scroll views at the same time.

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.

LGTM! Thank you for the contribution!

@davidmartos96
Copy link
Contributor Author

@Piinks Thanks to you!

@dnfield
Copy link
Contributor

dnfield commented Jun 16, 2021

It looks like this PR introduced issues for flutter_gallery. For example, just running flutter_gallery ends up triggering asserts introduced by this PR now.

@dnfield
Copy link
Contributor

dnfield commented Jun 16, 2021

I thould add that this seems to only happen on macOS desktop.

@davidmartos96
Copy link
Contributor Author

@dnfield I've just tested the flutter_gallery on Linux and I also get the asserts. But I'd say it is a legitimate warning. If you disable the asserts you will notice that scrolling with a mouse press in the main scroll in the home screen doesn't work.
From what I debugged that is because the PrimaryScrollController is being shared by multiple pieces of the UI at the same time, in particular:

  • The main scroll in the home page
  • The scroll inside the settings
  • Each of the 3 categories scrolls (Material, Cupertino and "Styles & Other")

The fact that it is such an "invisible" bug was the main reason I tried to improve the situation with this PR by warning about it. Everything works normally but it fails when interacting with the scrollbar either with the thumb on mobile or with the mouse on desktop.
Maybe @Piinks has some more information about this. The PR doesn't change the previous behavior, rather warns about the situation beforehand.

As an extra note, I think it will be great to further improve the message by including the location of the failing widget. In the case of the Flutter Gallery just in the home screen there are 5-6 active scrolls, so knowing which one is failing is very precious information.
I don't know who in the team has some expertise with how the Flutter errors and diagnostic nodes work so that the message can be improved. I asked on Discord the other day https://discord.com/channels/608014603317936148/608021234516754444/853999114018750484

@Piinks
Copy link
Contributor

Piinks commented Jun 16, 2021

scrolling with a mouse press in the main scroll in the home screen doesn't work.

@davidmartos96 can you confirm if this means trying to drag the scrollable with the mouse pointer? That is most likely disabled by ScrollBehavior.dragDevices on desktop if that is the case.

I'm filing a bug for this so we can discuss further, will link back in a sec

@Piinks
Copy link
Contributor

Piinks commented Jun 16, 2021

Also just connecting discussions - this came up here: https://discord.com/channels/608014603317936148/608021234516754444/854798350246215751

@Piinks
Copy link
Contributor

Piinks commented Jun 16, 2021

Let's follow-up here: flutter/gallery#523

@davidmartos96
Copy link
Contributor Author

davidmartos96 commented Jun 16, 2021

@davidmartos96 can you confirm if this means trying to drag the scrollable with the mouse pointer?

Yes, it is by dragging with the mouse pointer. Running the app on profile (no asserts) on Linux desktop will throw when dragging the thumb.

image

@nt4f04uNd
Copy link
Member

Could you please also check out this related issue #84735

@Piinks
Copy link
Contributor

Piinks commented Jun 16, 2021

Could you please also check out this related issue #84735

Followed up there.

@davidmartos96 davidmartos96 deleted the track_tap branch October 13, 2024 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: error message Error messages from the Flutter framework a: quality A truly polished experience f: cupertino flutter/packages/flutter/cupertino repository 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.

Error on scrollbar track tap when no controller attached
6 participants