skip to Main Content

I have a ListView.builder like so:

ListView.builder(
  key: _listViewKey,
  controller: widget.scrollController,
  shrinkWrap: true,
  scrollDirection: Axis.vertical,
  physics: const AlwaysScrollableScrollPhysics(),
  itemCount: state.items,
  itemBuilder: (context, index) {
  ...

Additionally I have a scroll to the end method that uses the scrollController to call animateTo to take the user to the bottom of the list regardless of where they are scrolled to. Here is that method:

void scrollToEnd() async {
   await Future.delayed(const Duration(milliseconds: 300));
   Future.doWhile(() {
     if (widget.scrollController.positions.last.extentAfter == 0) {
       return Future.value(false);
     }
     return widget.scrollController.animateTo(widget.scrollController.positions.last.maxScrollExtent, duration: Duration(milliseconds: 500), curve: Curves.linear).then((value) => true);
   });
}

I found that calling animateTo only once wasnt always enough and sometimes the scroll didnt go all the way to the bottom so this doWhile method was developed.

However now when scrolling up the list a few items and evoking scrollToEnd() there seems to be a pretty severe overscroll and total whitespace is shown until eventually the last list items sinks back down into view. I have 2 questions:

  1. Why is widget.scrollController.positions.last.maxScrollExtent ever returning a higher value than the list actually has?
  2. How can I stop this overscroll from happening? My only goal is that scrollToEnd() gets the list to the end every time and that is doesnt look wonky when it does it.

Edit: The heights of all the list items vary so I need a solution that doesnt work on hardcoded fixed heights.

2

Answers


  1. If I understand correctly what you are trying to do, which is to reach the end of the list, just change your _scrollToEnd function and ensure that it will only be called after the widget is built.

    Code

    void _scrollToEnd() {
      widget.scrollController.animateTo(
        widget.scrollController.position.maxScrollExtent,
        duration: Duration(milliseconds: 500),
        curve: Curves.linear,
      );
    }
    
    Login or Signup to reply.
  2. The AlwaysScrollablePhysics by default has a bouncing scroll physics, which causes the scroll controller to return a higher max scroll value. You can diminish the over scroll and also get precise values of the max scroll from scroll controller by clamping it.

    You can clamp the scroller as follows while keeping the always scroll physics:

    physics: AlwaysScrollableScrollPhysics(
                parent: ClampingScrollPhysics(),
              ),
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search