Search code examples
flutterflutter-animation

In Flutter, can I automatically scroll to top of grid view without the user being able to cancel the scroll?


In Flutter, I can use animateTo on the scroll controller to cause my grid view to scroll to zero offset over a set duration if I am some way down the view, but the animateTo method can be cancelled if the user does a scrolling action during the course of the scrolling to top animation (Notice in the documentation that it says 'An animation will be interrupted whenever the user attempts to scroll manually, or whenever another activity is started, or whenever the animation reaches the edge of the viewport and attempts to overscroll.'). Not only that, but if I put animateTo into an anonymous function for onTap in a GestureDetector, and then put a function right after it, that second function still gets executed even if the animateTo method is stopped because of the user doing a scrolling action before the animation finishes.

This is problematic for my situation. If the grid items are for written posts, I want the user to be able to click on the grid item, have the grid view scroll to top, fade out the grid view, and fade in content from the written post. Scrolling to the top before switching out the grid view for the content of the written post is important as I would like some header icons to animate and then disappear when the user scrolls some distance down, and for the header itself to move up after that header icon animation is triggered, resulting in more space for viewing content. To get the header icons back, the user would have to scroll up to a certain threshold, and then an appearance animation would play and the header icons would be back. So the user being able to cancel the animateTo method might result in those those icons not coming back when I want them to do so. BUT since the function right after the animateTo method still gets executed even if the animateTo method is cancelled, subsequent function(s) for fading out the grid view, removing its widget, replacing it with a new widget (that would have content from the written post), would still get executed (potentially leaving me with a new scroll controller at 0 but without those header icons??). Is there a way to scroll to the top of a grid view over a set duration (which duration would be set by a variable of mine that would change depending on the distance to be traveled in the scrolling to top) in a way that the user can NOT cancel? If there is no way to do this, then a consolation prize would be some way to prevent the subsequent function(s) for onTap of the Gesture Detector from being executed if the user stops the animateTo method from completing.

The jumpTo method will not work as it is not animated. I would have the name of my non-profit also shift and move as one scrolls down, so the icons getting back to an original position is also not the only thing dependent on scrolling back up.


Solution

  • With the help of comment(s) of @pskink, I was able to solve my problem with the use of IgnorePointer. I wrapped the screen Scaffold widget with IgnorePointer, set the ignoring value of IgnorePointer to the variable scrollAnmInProgress, set scrollAnmInProgress to true, did the scroll to top animation, and then set the scrollAnmInProgress value back to false. Here is some of the code that I used in the onTap method of the GestureDetector:

    scrollAnmInProgress = true;
    var scrollToTopTime = computeScrollToTopTime1(screenWidth,  _entriesGridScrollController.offset);
    var scrollToTopTimeInt = scrollToTopTime.round();
    controller.animateTo(0,
      duration: Duration(milliseconds: scrollToTopTimeInt),
      curve: Curves.easeInOut);
    var scrollAnmIgnorePointerTimer =
      Timer(Duration(milliseconds: scrollToTopTimeInt), () {
    scrollAnmInProgress = false;});
    

    The computeScrollToTopTime1 function was there to give me a duration time (whose value would depend on how much scrolling would have to be done) for the duration part of the animateTo method, which time was rounded to convert from a double type to the int type that is wanted by 'duration'. With that duration time in hand, I then started the scroll to top animation and I set a timer for the same time period as the duration of the animateTo method. When that timer was done (just at or around the time that the animateTo method finishes), the ignoring boolean value of IgnorePointer was set back to false, and the user could then scroll or do other things in the app. In the final app, I would likely call additional function(s) that would immediately set IgnorePointer's ignoring boolean value back to true to have the user wait for the fading out of the content and the fading in of new content.