EdsonBueno / infinite_scroll_pagination

Flutter package to help you lazily load and display pages of items as the user scrolls down your screen.
https://pub.dev/packages/infinite_scroll_pagination
MIT License
623 stars 211 forks source link

Calling retryLastFailedRequest doesn't recall the API #106

Closed DZESU closed 3 years ago

DZESU commented 3 years ago

Hello there, I kinda wonder if this is the expected behavior or it's a bug, when I call retryLastFailedRequest method it doesn't do anything. And upon looking at the code all it does it to set error = null. Is this when you intent it to be? How do I use this method?

EdsonBueno commented 3 years ago

Hi @DZESU , If you call retryLastFailedRequest when in an error state, it should request the last page again to the listener. If this is not happening, I'll need you to reproduce the issue in a minimal toy project so that I can help you. I'm closing the issue for now, but feel free to continue the discussion. Thank you

DZESU commented 3 years ago

Hi @EdsonBueno Here I able to reproduce a small sample code that causes this bug. What is found is changing childAspectRatio to bigger than 1, the retryLastFailedRequest function will work. But the childAspectRatio is kind of not consistent with my production project. Hope this helps you fix the bug. Any questions about the sample code please let me know.

import 'package:flutter/material.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  final PagingController<int, String> pagingController =
  PagingController(firstPageKey: 0, invisibleItemsThreshold: 10);

  int page = 0;

  @override
  void initState() {
    super.initState();
    pagingController.addPageRequestListener((pageKey) {
      page = pageKey;
      _appendList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Test List"),
      ),
      body: Container(
        child: RefreshIndicator(
          onRefresh: () async {
            pagingController.refresh();
          },
          child: CustomScrollView(
            slivers: [
              SliverPadding(
                padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                sliver: _itemListSection(context),
              ),
            ],
          ),
        ),
      ),
    );
  }

  _loading(){
    return Center(child: CircularProgressIndicator());
  }

  _Item(BuildContext context, String item) {
    return Container(
      padding: EdgeInsets.all(10),
      child: Text("item $item"),
    );
  }

  void _appendList() {
    List<String> items = List.generate(10, (index) => "${(pagingController?.itemList?.length??0) + index}");
    if((pagingController?.itemList?.length??0) < 60) {
      pagingController.appendPage(items, page + 1);
    }else{
      pagingController.error = Exception("Error Test");
    }
  }

  _errorItem() {
    return Container(
      color: Colors.green,
      child: TextButton(onPressed: (){
        pagingController.retryLastFailedRequest();
      }, child: Text("Retry",style: TextStyle(color: Colors.white),)),
    );
  }

  Widget _itemListSection(BuildContext context) {
    return PagedSliverGrid<int, String>(
      pagingController: pagingController,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          childAspectRatio: 3/4,
      ),
      builderDelegate: PagedChildBuilderDelegate<String>(
          itemBuilder: (context, item, index) => _Item(context, item),
          firstPageProgressIndicatorBuilder: (context) => _loading(),
          newPageProgressIndicatorBuilder: (context) => _loading(),
          noItemsFoundIndicatorBuilder: (context) => _errorItem(),
          newPageErrorIndicatorBuilder: (context) => _errorItem(),
          firstPageErrorIndicatorBuilder: (context) {
            return _errorItem();
          }),
    );
  }

}
EdsonBueno commented 3 years ago

Thanks @DZESU . I'll take a look.

rupinderjeet commented 2 years ago

@EdsonBueno I don't know if you will continue the development. But, I think this issue should be reopened so that it can receive some attention.

I have, also, stumbled on this issue. And, I think page_layout_builder.dart is the main decision maker in this case.

Here's the _buildListItemWidget() function from page_layout_builder.dart.

  /// Connects the [_pagingController] with the [_builderDelegate] in order to
  /// create a list item widget and request more items if needed.
  Widget _buildListItemWidget(
    BuildContext context,
    int index,
    List<ItemType> itemList,
  ) {
    if (!_hasRequestedNextPage) {
      final newPageRequestTriggerIndex =
          max(0, _itemCount - _invisibleItemsThreshold);

      final isBuildingTriggerIndexItem = index == newPageRequestTriggerIndex;

      if (_hasNextPage && isBuildingTriggerIndexItem) {
        // Schedules the request for the end of this frame.
        WidgetsBinding.instance?.addPostFrameCallback((_) {
          _pagingController.notifyPageRequestListeners(_nextKey!);
        });
        _hasRequestedNextPage = true;
      }
    }

Suppose, I have loaded some pages of total 33 items. After scrolling down in one fling, when I tried to load next page, there is an error. Following is the log just before the error:

[log] _buildListItemWidget: _hasNextPage: true, _itemCount: 36, index: 33, newPageRequestTriggerIndex: 33, _invisibleItemsThreshold: 3

Now, newPageErrorIndicator is visible. If I call retryLastFailedRequest() function, _buildListItemWidget() is called two times. Following is the log just after I called retryLastFailedRequest() function:

[log] _buildListItemWidget: _hasNextPage: true, _itemCount: 36, index: 34, newPageRequestTriggerIndex: 33, _invisibleItemsThreshold: 3 [log] _buildListItemWidget: _hasNextPage: true, _itemCount: 36, index: 35, newPageRequestTriggerIndex: 33, _invisibleItemsThreshold: 3

If I scroll a bit to top direction, I see following log:

[log] _buildListItemWidget: _hasNextPage: true, _itemCount: 36, index: 33, newPageRequestTriggerIndex: 33, _invisibleItemsThreshold: 3

This log triggers the next-page request because _invisibleItemsThreshold of 3 has been met (36 minus 33). When I called the retryLastFailedRequest() function, _buildListItemWidget() receives an index of 34 & 35, which skips the next-page request.

I do not know if you can help me, but I want to request you that if you see a possible solution, please let me know. At the end, if you do not, all I can try is to call following myself:

    // Schedules the request for the end of this frame.
    WidgetsBinding.instance?.addPostFrameCallback((_) {
      _pagingController.notifyPageRequestListeners(_nextKey!);
    });
    _hasRequestedNextPage = true;
vasilich6107 commented 4 months ago

Hi @EdsonBueno this seems to be still an issue