4

What I want

I want to create a behavior like Twitter App. So I scroll to the top of my list, I use a CupertinoSliverRefreshControl to load new posts and want to place them on top of my current shown list with keeping my ScrollPosition.

What my code looks like

To make usage of the CupertinoSliverRefreshControl I need to use slivers in a CustomScrollView. onRefresh I load new posts and add the old posts to it:

final _scrollController = ScrollController(keepScrollOffset: true);


return CustomScrollView(
  controller: _scrollController
  physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
  slivers: <Widget>[
    CupertinoSliverRefreshControl(
      onRefresh: () {
        // loading new posts. I set state with blocBuilder but for easiness I show it with setState here:
        setState(() { posts = List.from(newPosts..addAll(posts)); })
      }
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate((context, index) {
        return PostCard(posts[index]);
      },
      childCount: posts.length)
    )
  ]
);

What my problem is

Saving the ScrollPosition with a ScrollController is not working because it always saves the offset to the top and not to the bottom. In my case, the offset to the top will change by loading new posts.

What I've tried

I also tried to build the old posts and the new posts in two different SliverLists. To the SliverList with old posts, I added a key and set the center of my CustomScrollViewto this key. Doing so crashes my CupertinoSliverRefreshControl.

Also, I cannot just use the list reversed because I want a loading into the other direction as well in a further development state.

creativecreatorormaybenot
  • 114,516
  • 58
  • 291
  • 402
Sonius
  • 1,597
  • 3
  • 14
  • 33
  • 1
    Hello @Sonius! I'm exactly in the same place as you almost two years after. Did you find a way to avoid the jumpy listview when updating the scroll with the controller? I've found myself exactly in the same place as you when you wrote `So in a short frame it will jump to the top of the list and back to my position right after. The position after that jump is right`. Thanks! – Roc Boronat May 10 '21 at 15:25

1 Answers1

3

You can use ScrollController.position for this:

// You will need to place the following code snippets in appropriate locations.
double extentAfter;

// Save the current offset from the bottom:
extentAfter = _scrollController.position.extentAfter;


// After your loading is done, you can apply the saved extentAfter:
_scrollController.jumpTo(_scrollController.position.maxScrollExtent - extentAfter);

I do not exactly know how your setup works, but I could imagine that saving the extentAfter can be done in a listener of your ScrollController (because you want to have the latest extentAfter before the new items are inserted) and jumping to the "new" position when rebuilding the scroll view.

You can not call jumpTo when rebuilding by supplying a new ScrollController:

// Update your scroll controller with the new position.
_scrollController = ScrollController(
  initialScrollOffset: _scrollController.position.maxScrollExtent - extentAfter
);
creativecreatorormaybenot
  • 114,516
  • 58
  • 291
  • 402
  • 1
    Tried it works but is not optimal. I have to call _scrollController.jumpTo() after the rebuild. So in a short frame it will jump to the top of the list and back to my position right after. The position after that jump is right :) – Sonius Aug 13 '19 at 21:33
  • Or to specify my comment: Is there a way to call jumpTo right after the list was created? – Sonius Aug 13 '19 at 21:38
  • 1
    good idea but _scrollController.position.maxScrollExtent will not be updated before rebuilding because it is still assigned to the old list. – Sonius Aug 14 '19 at 07:40
  • 1
    @Sonius In that case you will have to calculate your new `maxScrollExtent` yourself. This should be fairly easy if your items have a fixed size. – creativecreatorormaybenot Aug 14 '19 at 08:20
  • They are not. But I think I can work with it, thank you! – Sonius Aug 14 '19 at 09:38
  • @Sonius the solution you are looking for is here, there is no delay or jumping, see Arsenii Burov answer https://stackoverflow.com/questions/52180947/prepend-list-view-items-while-maintaining-scroll-view-offset-in-flutter – mister_cool_beans Jan 07 '22 at 20:22