skip to Main Content

I have data for 100 items coming from my API server and the data is fine with no problem with it, but I’m trying to make it show 20 items every time I get to the end of the list but it keeps loading without stopping that’s my code.

  Widget build(BuildContext context) {
    return Center(
      child: NotificationListener<ScrollNotification>(
        onNotification: (ScrollNotification scrollInfo) {
          if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
              hasMore) {
            fetchItems();
          }
          return true;
        },
        child: FutureBuilder<void>(
          future: fetchItems(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(child: CircularProgressIndicator());
            } else if (snapshot.hasError) {
              return Center(child: Text('Error: ${snapshot.error}'));
            } else if (categoyItems == null || categoyItems!.isEmpty) {
              return const Center(child: Text('No states available'));
            } else {
              return GridView.custom(
                shrinkWrap: true,
                padding: const EdgeInsets.symmetric(vertical: 10),
                gridDelegate: SliverStairedGridDelegate(
                  mainAxisSpacing: 6,
                  crossAxisSpacing: 8,
                  pattern: [
                    const StairedGridTile(0.53, 0.8),
                    const StairedGridTile(0.470, 0.8),
                    const StairedGridTile(0.470, 0.8),
                    const StairedGridTile(0.53, 0.8),
                  ],
                ),
                childrenDelegate: SliverChildBuilderDelegate(
                  childCount: categoyItems!.length + (hasMore ? 1 : 0),
                  (context, index) {
                    if (index == categoyItems?.length) {
                      return const Center(child: CircularProgressIndicator());
                    }
                    final item = categoyItems![index];
                    return Container(....);
                  },
                ),
              );
            }
          },
        ),
      ),
    );
  }

and my function that get items from my API server

  List<CategoryItems>? categoyItems = [];
  int offset = 0;
  bool isLoading = false;
  bool hasMore = true;
  int totalCount = 0;
  bool hasData = false;

  @override
  void initState() {
    fetchItems();
    super.initState();
  }

  Future<void> fetchItems() async {
    if (isLoading) return;

    setState(() {
      isLoading = true;
    });

    try {
      PaginatedCategoryItems paginatedItems = await AppMethods()
          .fetchCategoryItems(widget.type!, widget.state!, offset);

        setState(() {
          categoyItems!.addAll(paginatedItems.items);
          offset += paginatedItems.items.length;
          totalCount = paginatedItems.totalCount;
          isLoading = false;
          hasMore = categoyItems!.length < totalCount;
          hasData = true;
        });
      
    } catch (err) {
      if (kDebugMode) {
        print(err);
      }
      setState(() {
        isLoading = false;
      });
    }
  }

can someone help me, please?

2

Answers


  1. future: fetchItems() can create infinite loop. I prefer using diff logic/bloc for the pagination.

    This is how it goes

    class _DTPagState extends State<DTPag> {
      /// mostly I shift theses to a bloc
      bool isLoading = false;
      List<int> myCurrentData = [];
    
      Future fetchItems() async {
        /// you can also return like `if(isLoading) return;` 
        isLoading = true;
        setState(() {});
    
        ///load data by api call
    
        isLoading = false;
    
        ///fetched items
        myCurrentData.addAll([]);
    
        setState(() {});
      }
    
      @override
      void initState() {
        super.initState();
        fetchItems(); //first time fetch
      }
    
      @override
      Widget build(BuildContext context) {
        return NotificationListener<ScrollNotification>(
          onNotification: (ScrollNotification scrollInfo) {
            if (YourCondition) {
              fetchItems();
            }
            return true;
          },
          child: ListView.builder(
            itemCount: myCurrentData.length + (isLoading ? 1 : 0),
            itemBuilder: (context, index) {
              if (index == myCurrentData.length) {
                return Center(child: CircularProgressIndicator());
              }
              return Text(myCurrentData[index].toString());
            },
          ),
        );
      }
    }
    
    Login or Signup to reply.
  2. try this Infinite_scroll_pagination

    and this is example code

    handleFetchMeme() in controller should return in paginate model like this model

    import 'package:freezed_annotation/freezed_annotation.dart';
    
    import '../meme_enitity/meme.entity.dart';
    
    part 'meme_pagination.entity.freezed.dart';
    part 'meme_pagination.entity.g.dart';
    
    @freezed
    class MemePaginationEntity with _$MemePaginationEntity {
      const factory MemePaginationEntity({
        @Default(0) num page,
        @Default(0) num perPage,
        @Default([]) List<MemeEntity> memeList,
      }) = _MemePaginationEntity;
    
      factory MemePaginationEntity.fromJson(Map<String, Object?> json)
          => _$MemePaginationEntityFromJson(json);
    }
    

    Example GetXController

    import 'package:get/get.dart';
    import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
    
    import '../../../domain/model/meme_enitity/meme.entity.dart';
    
    class PlaygroundPageController extends GetxController {
      @override
      void onInit() {
        pagingController.addPageRequestListener((pageKey) {
          handleGetMemeList(page: pageKey);
        });
        super.onInit();
      }
    
      final pagingController = PagingController<int, MemeEntity>(firstPageKey: 1);
    
      Future<void> handleGetMemeList({required int page}) async {
        try {
          final res = handleFetchMeme(page: page, perPage: 10);
          if (res.memeList.length < 10) {
            pagingController.appendLastPage(res.memeList);
          } else {
            pagingController.appendPage(res.memeList, ++page);
          }
        } catch (err) {
         pagingController.error = err;
        }
      }
    }
    

    Widget

    class PlaygroundPage extends StatelessWidget {
      const PlaygroundPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return GetBuilder<PlaygroundPageController>(
          global: false,
          init: PlaygroundPageController(),
          builder: (controller) => Scaffold(
            backgroundColor: Colors.grey[200],
            appBar: CustomAppBar(
              backgroundColor: Colors.grey[500],
              title: '',
            ),
            body: Column(
              children: [
                PagedListView(
                  pagingController: controller.pagingController,
                  builderDelegate: PagedChildBuilderDelegate<MemeEntity>(
                    itemBuilder: (context, item, index) {
                      return Container(
                         child : Text(item.memeName)
                       ); // Your Widget
                    },
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search