Skip to content
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

[Feature Request] Scroll to previous pages when setting firstPageKey #56

Open
vintage opened this issue Feb 19, 2021 · 39 comments
Open
Labels
feature New feature or request

Comments

@vintage
Copy link

vintage commented Feb 19, 2021

The PagingController can be built with firstPageKey to indicate initial page to load. Sounds great, but how to "scroll back" to previous pages then?

I'm implementing a players ranking list where the initial page is dynamically set based on position of current user in the leaderboard.

Example

User enters the screen and sees 5th page of the results. He can scroll down to see further pages (6,7,8), but I have to provide some mechanism to be able to scroll up (to pages 4,3,2,1). Is that ever possible?

Already tried to use addStatusListener/addListener, but nothing interesting is triggered there.

@EdsonBueno
Copy link
Owner

Hi @vintage .
This is currently not possible, but you're the third one asking for this feature.
If more demand comes in, I'll definitely give it a shot.

@vintage
Copy link
Author

vintage commented Feb 19, 2021

Well, so why closing all of the issues instead of keeping one of these open for discussion or even making it visible to the community that help is wanted?

@EdsonBueno
Copy link
Owner

I was going to use another issue for this @vintage , but let's use this one. I think you explained what you want in a better way.

@NamanShergill
Copy link

I would love this as well!

@tobysimone

This comment was marked as duplicate.

@alejandrogiubel

This comment was marked as duplicate.

@wizard11

This comment was marked as duplicate.

@NamanShergill
Copy link

Is this on the roadmap/planned for future? This will be a great feature to have to have a much better control over the pagination.

@EdsonBueno
Copy link
Owner

EdsonBueno commented May 27, 2021

Yes, @NamanShergill, it definitely is! I just couldn't allocate the time for it yet, stuffed with work.

@NamanShergill
Copy link

Yes, @NamanShergill, it definitely is! I just couldn't allocate the time for it yet, stuffed with work.

That's awesome!

@sabetAI
Copy link

sabetAI commented Jun 1, 2021

@EdsonBueno any estimate of when it can be available?

@DesmondFox

This comment was marked as duplicate.

@sebastianopighi

This comment was marked as duplicate.

5 similar comments
@zzwx

This comment was marked as duplicate.

@changlan

This comment was marked as duplicate.

@sopherwang

This comment was marked as duplicate.

@dush999

This comment was marked as duplicate.

@dushmantha-b

This comment was marked as duplicate.

@dushmantha-b

This comment was marked as duplicate.

@mohamadhadibi

This comment was marked as duplicate.

@tomasweigenast

This comment was marked as duplicate.

@IvanAleksandrov94
Copy link

At the moment, for lazy loading in both directions, you can try using the implementation with Viewport.

class LoadMoreUpAndDown extends StatefulWidget {
  const LoadMoreUpAndDown({Key? key}) : super(key: key);

  @override
  _LoadMoreUpAndDownState createState() => _LoadMoreUpAndDownState();
}

class _LoadMoreUpAndDownState extends State<LoadMoreUpAndDown> {
  final Key downListKey = UniqueKey();
  static const _pageSize = 20;

  final PagingController<int, Message> _pagingReplyUpController = PagingController(
    firstPageKey: 0,
  );
  final PagingController<int, Message> _pagingReplyDownController = PagingController(
    firstPageKey: 0,
  );

  Future<void> _fetchUpPage(int pageKey) async {
    try {
      final newItems = await RemoteApi.getMessagesList(pageKey, _pageSize);
      final isLastPage = newItems.length < _pageSize;
      if (isLastPage) {
        _pagingReplyUpController.appendLastPage(newItems);
      } else {
        final nextPageKey = pageKey + newItems.length;
        _pagingReplyUpController.appendPage(newItems, nextPageKey);
      }
    } catch (error) {
      _pagingReplyUpController.error = error;
    }
  }

  Future<void> _fetchDownPage(int pageKey) async {
    try {
      final newItems = await RemoteApi.getMessagesList(pageKey, _pageSize);
      final isLastPage = newItems.length < _pageSize;
      if (isLastPage) {
        _pagingReplyDownController.appendLastPage(newItems);
      } else {
        final nextPageKey = pageKey + newItems.length;
        _pagingReplyDownController.appendPage(newItems, nextPageKey);
      }
    } catch (error) {
      _pagingReplyDownController.error = error;
    }
  }

  @override
  void initState() {
    super.initState();
    _pagingReplyUpController.addPageRequestListener((pageKey) {
      _fetchUpPage(pageKey);
    });

    _pagingReplyDownController.addPageRequestListener((pageKey) {
      _fetchDownPage(pageKey);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scrollable(
      viewportBuilder: (BuildContext context, ViewportOffset position) {
        return Viewport(
          offset: position,
          center: downListKey,
          slivers: [
            PagedSliverList(
              pagingController: _pagingReplyUpController,
              builderDelegate: PagedChildBuilderDelegate<Message>(
                itemBuilder: (context, messageUp, index) => SizedBox(
                  height: 50,
                  width: double.infinity,
                  child: Text(
                    '${messageUp.text}',
                  ),
                ),
              ),
            ),
            PagedSliverList(
              key: downListKey,
              pagingController: _pagingReplyDownController,
              builderDelegate: PagedChildBuilderDelegate<Message>(
                itemBuilder: (context, messageDown, index) => SizedBox(
                  height: 50,
                  width: double.infinity,
                  child: Text(
                    '${messageDown.text}',
                  ),
                ),
              ),
            ),
          ],
        );
      },
    );
  }

  @override
  void dispose() {
    _pagingReplyUpController.dispose();
    _pagingReplyDownController.dispose();
    super.dispose();
  }
}

@PaulRudin
Copy link

I'm in a similar situation to the OP. I have potentially large number of items (possibly hundreds of thousands) representing the rankings of players in a game. I want the initial view to show the position of the logged in player, and to be able to scroll up and down from that position.

@gorkemsari

This comment was marked as duplicate.

@bobbyqul

This comment was marked as duplicate.

@EdsonBueno
Copy link
Owner

EdsonBueno commented Oct 18, 2021

Can any of you confirm @IvanAleksandrov94's implementation is what you're looking for?
If yes, I can expand on it and make it into the package.
If not, what's missing?

@desmeit

This comment was marked as duplicate.

@liemeon

This comment was marked as duplicate.

1 similar comment
@jesusmartinoza

This comment was marked as duplicate.

@liemeon

This comment was marked as duplicate.

@vishalknishad

This comment was marked as duplicate.

@lauglam

This comment was marked as duplicate.

2 similar comments
@ChiPhanTlm

This comment was marked as duplicate.

@jefcolbi

This comment was marked as duplicate.

@gonzalogauto

This comment was marked as duplicate.

@clragon

This comment was marked as duplicate.

@amanokerim
Copy link

Can any of you confirm @IvanAleksandrov94's implementation is what you're looking for? If yes, I can expand on it and make it into the package. If not, what's missing?

I think @IvanAleksandrov94 's solution on the package would be nice!

@mgudev7a
Copy link

Implementation from @IvanAleksandrov94 is working. Adopted it for navigation in calendar items via DateTime.

For the case of a completly empty list there will be two empty list indicators.
To prevent this I use a SizedBox(height:0) as noItemsFoundIndicatorBuilder for the PagedSliverLists and added an additional widget / exchanged the center key. My solution to identify the empty list is rather brutal (joining empty list bool states from the two different fetch functions) - Perhaps a packaged solution can address this in a more elegant way.

Anyway thanks @EdsonBueno for the package and Ivan for the Viewport usage!

@vergaraSC
Copy link

How Can I prevent scroll jump when i dro

At the moment, for lazy loading in both directions, you can try using the implementation with Viewport.

class LoadMoreUpAndDown extends StatefulWidget {
  const LoadMoreUpAndDown({Key? key}) : super(key: key);

  @override
  _LoadMoreUpAndDownState createState() => _LoadMoreUpAndDownState();
}

class _LoadMoreUpAndDownState extends State<LoadMoreUpAndDown> {
  final Key downListKey = UniqueKey();
  static const _pageSize = 20;

  final PagingController<int, Message> _pagingReplyUpController = PagingController(
    firstPageKey: 0,
  );
  final PagingController<int, Message> _pagingReplyDownController = PagingController(
    firstPageKey: 0,
  );

  Future<void> _fetchUpPage(int pageKey) async {
    try {
      final newItems = await RemoteApi.getMessagesList(pageKey, _pageSize);
      final isLastPage = newItems.length < _pageSize;
      if (isLastPage) {
        _pagingReplyUpController.appendLastPage(newItems);
      } else {
        final nextPageKey = pageKey + newItems.length;
        _pagingReplyUpController.appendPage(newItems, nextPageKey);
      }
    } catch (error) {
      _pagingReplyUpController.error = error;
    }
  }

  Future<void> _fetchDownPage(int pageKey) async {
    try {
      final newItems = await RemoteApi.getMessagesList(pageKey, _pageSize);
      final isLastPage = newItems.length < _pageSize;
      if (isLastPage) {
        _pagingReplyDownController.appendLastPage(newItems);
      } else {
        final nextPageKey = pageKey + newItems.length;
        _pagingReplyDownController.appendPage(newItems, nextPageKey);
      }
    } catch (error) {
      _pagingReplyDownController.error = error;
    }
  }

  @override
  void initState() {
    super.initState();
    _pagingReplyUpController.addPageRequestListener((pageKey) {
      _fetchUpPage(pageKey);
    });

    _pagingReplyDownController.addPageRequestListener((pageKey) {
      _fetchDownPage(pageKey);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scrollable(
      viewportBuilder: (BuildContext context, ViewportOffset position) {
        return Viewport(
          offset: position,
          center: downListKey,
          slivers: [
            PagedSliverList(
              pagingController: _pagingReplyUpController,
              builderDelegate: PagedChildBuilderDelegate<Message>(
                itemBuilder: (context, messageUp, index) => SizedBox(
                  height: 50,
                  width: double.infinity,
                  child: Text(
                    '${messageUp.text}',
                  ),
                ),
              ),
            ),
            PagedSliverList(
              key: downListKey,
              pagingController: _pagingReplyDownController,
              builderDelegate: PagedChildBuilderDelegate<Message>(
                itemBuilder: (context, messageDown, index) => SizedBox(
                  height: 50,
                  width: double.infinity,
                  child: Text(
                    '${messageDown.text}',
                  ),
                ),
              ),
            ),
          ],
        );
      },
    );
  }

  @override
  void dispose() {
    _pagingReplyUpController.dispose();
    _pagingReplyDownController.dispose();
    super.dispose();
  }
}

I don't know if it happens to anyone else but when I scroll from bottom to top the scroll makes a sharp jump to the first item in the top list, how can I prevent the scroll from making those jumps? thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

No branches or pull requests