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
625 stars 213 forks source link

addPageRequestListener always fetching page even no scrolling #302

Closed AndersonArvando closed 8 months ago

AndersonArvando commented 10 months ago
class DashboardWidget extends StatefulWidget {
  const DashboardWidget({
    super.key,
  });

  @override
  State<DashboardWidget> createState() => _DashboardWidgetState();
}

class _DashboardWidgetState extends State<DashboardWidget> {
  dynamic leads = null;
  dynamic creditBalance = null;
  dynamic managers;

  @override
  void initState() {
    initData();

    super.initState();
  }

  Future<void> initData() async {
    final token = Application.preferences?.getString('api_token') ?? "";
    final response = await Api.getLeads(token: token);
    dynamic managerIds;
    if (isUserManager()) {
      var managers = await Api.getUserSettings(
        token: token,
        params: {"manager": Application.preferences?.getString('id')},
      );

      managers = jsonDecode(managers.data)['data'];
      managerIds = managers.map((e) => e['id']).toList();
    }

    final partner =
        jsonDecode(Application.preferences?.getString('partner') ?? "");

    final managersData = await Api.getUserSettings(
      token: token,
    );
    if (mounted) {
      setState(() {
        creditBalance = partner['partner_credit_balance'].replaceAllMapped(
          RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
          (Match match) => '${match[1]},',
        );
        leads = jsonDecode(response.data);

        if (isUserDesigner() ||
            (isUserDirector() &&
                Application.preferences!.getBool('company_view') == false)) {
          leads = leads
              .where(
                (lead) =>
                    lead['manager_id'] ==
                    int.parse(Application.preferences?.getString('id') ?? ''),
              )
              .toList();
        } else if (isUserManager()) {
          leads = leads
              .where((lead) => managerIds.contains(lead['manager_id']) as bool)
              .toList();
        }

        leads = leads
            .where(
              (lead) => [
                'Follow up (uncontactable)',
                'Follow up (discussion)',
                'New',
              ].contains(lead['heat_type_name']),
            )
            .toList();

        leads = groupBy(
          leads,
          (obj) => (obj! as Map<String, dynamic>)['grouped_lead_won_date'],
        );

        managers = jsonDecode(managersData.data)['data'].where((e) {
          if (isUserManager()) {
            return e['manager_id'] ==
                    Application.preferences?.getString('id') ||
                e['id'].toString() == Application.preferences?.getString('id');
          } else {
            return true;
          }
        }).toList();
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      color: dangerColor,
      onRefresh: () async {
        if (mounted) {
          setState(() {
            leads = null;
            creditBalance = null;
            initData();
          });
        }
      },
      child: CustomScrollView(
        slivers: [
          SliverToBoxAdapter(
            child: Container(
              padding: const EdgeInsets.all(20),
              child: Column(
                children: [
                  IntrinsicHeight(
                    child: Row(
                      children: [
                        FollowupLeads(
                          counter:
                              leads != null ? leads.length.toString() : "0",
                        ),
                        TotalRewards(
                          counter:
                              leads != null ? creditBalance.toString() : "0",
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(
                    height: 30,
                  ),
                  const SizedBox(height: 20),
                ],
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildListDelegate([
              Center(
                child: LeadsTable(leads: leads, managers: managers),
              ),
            ]),
          )
        ],
      ),
    );
  }
}

class LeadsTable extends StatefulWidget {
  const LeadsTable({super.key, required this.leads, required this.managers});
  final dynamic leads;
  final dynamic managers;

  @override
  State<LeadsTable> createState() => _LeadsTableState();
}

class _LeadsTableState extends State<LeadsTable> {
  static const _pageSize = 2;

  final PagingController<int, dynamic> _pagingController =
      PagingController(firstPageKey: 0);

  @override
  void initState() {
    _pagingController.addPageRequestListener((pageKey) {
      _fetchPage(pageKey);
    });

    super.initState();
  }

  Future<void> _fetchPage(int pageKey) async {
    try {
      if (pageKey < 30) {
        final token = Application.preferences?.getString('api_token') ?? "";
        var newItems = await Api.getLeads(token: token, params: {
          "pageKey": pageKey,
          "pageLimit": _pageSize,
          "status": {"0": 3, "1": 4, "2": 42}
        });

        newItems = groupBy(
          jsonDecode(newItems.data),
          (obj) => (obj! as Map<String, dynamic>)['grouped_lead_won_date'],
        );
        var list = [];
        var length = 0;
        newItems.forEach((k, v) => {
              list.add(LeadModel(k, v)),
              length += v.length as int,
            });

        final isLastPage = length < _pageSize;
        if (isLastPage) {
          _pagingController.appendLastPage(list);
        } else {
          final nextPageKey = pageKey + length;
          _pagingController.appendPage(list, nextPageKey as int?);
        }
      }
    } catch (error) {
      _pagingController.error = error;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(20),
      ),
      child: Column(
        children: [
          PagedListView<int, dynamic>(
            // physics: const NeverScrollableScrollPhysics(),
            scrollDirection: Axis.vertical,
            primary: false,
            shrinkWrap: true,
            pagingController: _pagingController,
            builderDelegate: PagedChildBuilderDelegate<dynamic>(
              itemBuilder: (context, index, item) {
                String key = index.date;
                return Column(
                  children: [
                    Container(
                      alignment: Alignment.centerLeft,
                      padding: const EdgeInsets.symmetric(
                        horizontal: 20,
                        vertical: 15,
                      ),
                      decoration: const BoxDecoration(
                        border: Border(
                          bottom: BorderSide(
                            width: 1,
                            color: Color(0xffE5E5E5),
                          ),
                        ),
                      ),
                      child: Helvetica(
                        text: DateFormat('dd LLLL yyyy')
                            .format(DateTime.parse(key)),
                        color: const Color(0xff546889),
                      ),
                    ),
                  ],
                );
              },
            ),
          )
        ],
      ),
    );
  }

  @override
  void dispose() {
    _pagingController.dispose();
    super.dispose();
  }
}

i've tried to add customscrollview but it's still loading even i'm not doing scrolling or load more action on the screen can anyone help me please i've been stuck for so long

AndersonArvando commented 10 months ago

@EdsonBueno @clragon can help to solve my issue please

Israel001 commented 10 months ago

Yeah I'm also looking for solution to this issue. Please kindly share if you see any.

caiombueno commented 10 months ago

Hi @AndersonArvando. I tried to replicate your issue using this piece of code:

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: CustomScrollView(
            slivers: [
              SliverList(
                delegate: SliverChildListDelegate([
                  const Center(
                    child: Column(
                      children: [
                        MyPagedListView(),
                      ],
                    ),
                  ),
                ]),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class MyPagedListView extends StatefulWidget {
  const MyPagedListView({
    super.key,
  });

  @override
  State<MyPagedListView> createState() => _MyPagedListViewState();
}

class _MyPagedListViewState extends State<MyPagedListView> {
  late final PagingController<int, int> _pagingController;

  @override
  void initState() {
    _pagingController = PagingController(firstPageKey: 0);

    _pagingController.addPageRequestListener((pageKey) {
      print('fetch');
      _fetchPage(pageKey);
    });

    super.initState();
  }

  Future<void> _fetchPage(int pageKey) async {
    const pageSize = 20;
    List<int> newItems = [];
    for (int i = pageSize * pageKey; i < pageSize; i++) {
      newItems.add(i);
    }

    _pagingController.appendPage(newItems, pageKey++);
  }

  @override
  Widget build(BuildContext context) {
    return PagedListView(
      shrinkWrap: true,
      pagingController: _pagingController,
      builderDelegate: PagedChildBuilderDelegate(
        itemBuilder: (context, data, index) => Container(
          height: 100.0,
          color: Colors.red,
          child: Text('Child $index'),
        ),
      ),
    );
  }
}

The widget tree is currently represented as: CustomScrollView -> SliverList -> Column -> PagedListView

However, when using CustomScrollViews, it's recommended to use PagedSliverList instead.

So, it would be like:

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: CustomScrollView(
            slivers: [
              MyPagedSliverList(),
            ],
          ),
        ),
      ),
    );
  }
}

class MyPagedSliverList extends StatefulWidget {
  const MyPagedSliverList({
    super.key,
  });

  @override
  State<MyPagedSliverList> createState() => _MyPagedSliverListState();
}

class _MyPagedSliverListState extends State<MyPagedSliverList> {
  late final PagingController<int, int> _pagingController;

  @override
  void initState() {
    _pagingController = PagingController(firstPageKey: 0);

    _pagingController.addPageRequestListener((pageKey) {
      print('fetch');
      _fetchPage(pageKey);
    });

    super.initState();
  }

  Future<void> _fetchPage(int pageKey) async {
    const pageSize = 20;
    List<int> newItems = [];
    for (int i = pageSize * pageKey; i < pageSize; i++) {
      newItems.add(i);
    }

    _pagingController.appendPage(newItems, pageKey++);
  }

  @override
  Widget build(BuildContext context) {
    return PagedSliverList(
      pagingController: _pagingController,
      builderDelegate: PagedChildBuilderDelegate(
        itemBuilder: (context, data, index) => Container(
          height: 100.0,
          color: Colors.red,
          child: Text('Child $index'),
        ),
      ),
    );
  }
} 

Now, the widget tree is something similar to this: CustomScrollView -> PagedSliverList

I hope this helps to solve your problem.

Israel001 commented 10 months ago

@caiombueno

Thanks for your suggestion, but your solution doesn't work for me.

I've tried it. I am not using a CustomScrollView though so I don't know if that has something to do with it or not.

Here is how my code currently looks like:

class ViewAllSubscriptions extends StatefulWidget {
  final int streetId;
  final String status;
  final String month;
  final String streetName;
  final String area;

  const ViewAllSubscriptions({
    Key? key,
    required this.streetId,
    required this.month,
    required this.status,
    required this.streetName,
    required this.area,
  }) : super(key: key);

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

class ViewAllSubscriptionsState extends State<ViewAllSubscriptions> {
  static const _pageSize = 3;
  String _selectedItem = "ASC";
  String searchText = "";
  String previousSearchText = "";
  bool searchMode = false;
  TextEditingController searchController = TextEditingController();

  final PagingController<int, dynamic> _pagingController = PagingController(
    firstPageKey: 1,
  );

  @override
  void dispose() {
    _pagingController.dispose();
    super.dispose();
  }

  initPSPSubscriptionDetails(ProfileViewModel model, int pageKey) async {
    bool isLastPage = await model.fetchPSPSubscriptionDetails(
      widget.streetId,
      widget.month,
      widget.status,
      searchText,
      Pagination(
        page: pageKey,
        limit: _pageSize,
        orderBy: widget.status == "paid" ? "createdAt" : "houseNumber",
        orderDir: _selectedItem,
      ),
    );
    if (isLastPage) {
      _pagingController.appendLastPage(model.subDetails);
    } else {
      _pagingController.appendPage(
        model.subDetails,
        pageKey + 1,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return BaseWidget<ProfileViewModel>(
      viewModelBuilder: () => ProfileViewModel(context),
      onStart: (model) {
        initPSPSubscriptionDetails(model, 1);
        _pagingController.addPageRequestListener((pageKey) async {
          initPSPSubscriptionDetails(model, pageKey);
        });
      },
      builder: (context, model, wgt) {
        if (!model.canDisplayPage) {
          return const CustomLoader();
        }
        return RefreshIndicator(
          child: Scaffold(
            appBar: AppBar(
              scrolledUnderElevation: 0,
              toolbarHeight: 10.h,
              centerTitle: true,
              leading: IconButton(
                icon: const Icon(Icons.arrow_back_ios),
                onPressed: () {
                  model.canDisplayPage = true;
                  Navigator.pop(context);
                },
              ),
              title: Column(
                children: [
                  Container(
                    alignment: Alignment.bottomCenter,
                    child: Text(
                      widget.streetName,
                      overflow: TextOverflow.ellipsis,
                      style: Theme.of(context).textTheme.headlineMedium,
                    ),
                  ),
                  Center(
                    child: Text(
                      widget.area,
                      overflow: TextOverflow.ellipsis,
                      style: Theme.of(context).textTheme.bodySmall,
                    ),
                  ),
                ],
              ),
              actions: [
                IconButton(
                  onPressed: () {},
                  icon: const Icon(Icons.search),
                ).opacity(opacity: 0),
              ],
            ),
            body: Column(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: model.subDetails.isEmpty && !searchMode
                  ? MainAxisAlignment.center
                  : MainAxisAlignment.start,
              children: [
                model.subDetails.isEmpty && !searchMode
                    ? const SizedBox(height: 0)
                    : Row(
                        mainAxisSize: MainAxisSize.max,
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Container(
                            width: 40.w,
                            color: AppColors.kColorGrey,
                            alignment: Alignment.center,
                            child: Row(
                              mainAxisSize: MainAxisSize.max,
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                searchText.isNotEmpty
                                    ? IconButton(
                                        icon: const Icon(Icons.close_rounded),
                                        onPressed: () {
                                          searchController.text = "";
                                          bool previousSearchMode = searchMode;
                                          setState(() {
                                            searchMode = false;
                                            searchText = "";
                                            previousSearchText = "";
                                          });
                                          if (previousSearchMode) {
                                            if (_pagingController.nextPageKey !=
                                                _pagingController
                                                    .firstPageKey) {
                                              _pagingController.refresh();
                                              if (model.subDetails.isEmpty) {
                                                initPSPSubscriptionDetails(
                                                  model,
                                                  1,
                                                );
                                              }
                                            } else {
                                              initPSPSubscriptionDetails(
                                                model,
                                                1,
                                              );
                                            }
                                          }
                                        },
                                      ).withWidth((2.5).h)
                                    : const SizedBox(),
                                FormItem(
                                  backgroundColor: Colors.transparent,
                                  isDesignTwo: true,
                                  maxLines: 1,
                                  placeholder: 'Enter House No',
                                  label: 'Enter House No',
                                  controller: searchController,
                                  onChange: (val) {
                                    setState(() => searchText = val);
                                    if (val.isEmpty) {
                                      bool previousSearchMode = searchMode;
                                      setState(() {
                                        searchMode = false;
                                        searchText = "";
                                        previousSearchText = "";
                                      });
                                      if (previousSearchMode) {
                                        if (_pagingController.nextPageKey !=
                                            _pagingController.firstPageKey) {
                                          _pagingController.refresh();
                                          if (model.subDetails.isEmpty) {
                                            initPSPSubscriptionDetails(
                                              model,
                                              1,
                                            );
                                          }
                                        } else {
                                          initPSPSubscriptionDetails(
                                            model,
                                            1,
                                          );
                                        }
                                      }
                                    }
                                  },
                                  keyboardType: EnumKeyboardType.number,
                                ).expand(),
                                Container(
                                  color: Colors.black,
                                  child: IconButton(
                                    onPressed: () {
                                      if (searchText.isNotEmpty &&
                                          (previousSearchText.isEmpty ||
                                              previousSearchText !=
                                                  searchText)) {
                                        setState(() {
                                          searchMode = true;
                                          previousSearchText = searchText;
                                        });
                                        if (_pagingController.nextPageKey !=
                                            _pagingController.firstPageKey) {
                                          _pagingController.refresh();
                                          if (model.subDetails.isEmpty) {
                                            initPSPSubscriptionDetails(
                                              model,
                                              1,
                                            );
                                          }
                                        } else {
                                          initPSPSubscriptionDetails(model, 1);
                                        }
                                      }
                                    },
                                    icon: Icon(
                                      Icons.search,
                                      color: AppColors.kColorWhite,
                                    ),
                                  ),
                                )
                              ],
                            ),
                          ).cornerRadiusWithClipRRect(5.sp).withWidth(210),
                          Row(
                            children: [
                              Text(
                                "Total: ${model.subDetailsTotal}",
                                style: const TextStyle(
                                  fontWeight: FontWeight.bold,
                                ),
                              ).paddingRight(2.h),
                              DropdownButtonHideUnderline(
                                child: DropdownButton2(
                                  customButton:
                                      const Icon(Icons.more_vert_outlined),
                                  items: [
                                    DropdownMenuItem<String>(
                                      enabled: _selectedItem == "DESC",
                                      value: "ASC",
                                      child: Row(
                                        mainAxisSize: MainAxisSize.max,
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        children: [
                                          Text(
                                            "Sort by ASC",
                                            style: TextStyle(
                                              fontSize: 8.sp,
                                            ),
                                          ).expand(),
                                          if (_selectedItem == "ASC")
                                            const Icon(Icons.check),
                                        ],
                                      ),
                                    ),
                                    DropdownMenuItem<String>(
                                      enabled: _selectedItem == "ASC",
                                      value: "DESC",
                                      child: Row(
                                        mainAxisSize: MainAxisSize.max,
                                        children: [
                                          Text(
                                            "Sort by DESC",
                                            style: TextStyle(fontSize: 8.sp),
                                          ).expand(),
                                          if (_selectedItem == "DESC")
                                            const Icon(Icons.check),
                                        ],
                                      ),
                                    ),
                                  ],
                                  onChanged: (value) {
                                    _selectedItem = value!;
                                    _pagingController.refresh();
                                    if (model.subDetails.isEmpty) {
                                      initPSPSubscriptionDetails(model, 1);
                                    }
                                  },
                                  dropdownStyleData: DropdownStyleData(
                                    width: 160,
                                    padding: const EdgeInsets.symmetric(
                                      vertical: 0,
                                    ),
                                    decoration: BoxDecoration(
                                      borderRadius: BorderRadius.circular(4),
                                      color: Colors.white,
                                    ),
                                    offset: const Offset(0, 8),
                                  ),
                                  menuItemStyleData: const MenuItemStyleData(
                                    padding: EdgeInsets.only(
                                      left: 16,
                                      right: 16,
                                    ),
                                  ),
                                ),
                              ),
                            ],
                          )
                        ],
                      )
                        .withHeight(5.h)
                        .withWidth(100.w)
                        .paddingSymmetric(vertical: 1.h, horizontal: 5.w),
                model.subDetails.isEmpty
                    ? const SizedBox(height: 0)
                    : Row(
                        mainAxisSize: MainAxisSize.max,
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          RichText(
                            text: TextSpan(
                              children: [
                                TextSpan(
                                  text: "₦ ",
                                  style: TextStyle(
                                    color: Colors.black,
                                    fontSize: 14.sp,
                                    fontWeight: FontWeight.w600,
                                  ),
                                ),
                                TextSpan(
                                  text: model.subDetails.isEmpty ||
                                          widget.status != 'paid'
                                      ? '0.00'
                                      : MoneyFormatter(
                                          amount: model.subDetails
                                              .fold(
                                                0,
                                                (t, e) =>
                                                    t +
                                                    (e['payment']
                                                            ['serviceCharge']
                                                        as int),
                                              )
                                              .toDouble(),
                                        ).output.nonSymbol,
                                  style: Theme.of(context)
                                      .textTheme
                                      .headlineMedium,
                                )
                              ],
                            ),
                          ),
                          WasteNgButton(
                            text: widget.status.capitalizeFirstLetter(),
                            onPressed: () {},
                            backgroundColor: widget.status == "paid"
                                ? AppColors.kColorPrimary
                                : Colors.red,
                          ),
                        ],
                      )
                        .withHeight(5.h)
                        .withWidth(100.w)
                        .paddingSymmetric(vertical: 1.h, horizontal: 5.w),
                model.subDetails.isNotEmpty
                    ? const SizedBox(height: 0)
                    : Center(
                        child: Text(
                          "No Record",
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 14.sp,
                          ),
                        ),
                      ).paddingBottom(30.h).paddingTop(
                          model.subDetails.isEmpty && !searchMode ? 0.h : 10.h,
                        ),
                model.subDetails.isEmpty
                    ? const SizedBox(height: 0)
                    : PagedSliverList<int, dynamic>(
                        pagingController: _pagingController,
                        builderDelegate: PagedChildBuilderDelegate<dynamic>(
                          itemBuilder: (context, item, index) {
                            return _WidgetSubscription(
                              item: item,
                              status: widget.status,
                            );
                          },
                        ),
                      ).expand()
              ],
            ),
          ),
          onRefresh: () async {
            searchController.text = "";
            setState(() => searchText = "");
            await Future.delayed(const Duration(seconds: 2));
            _pagingController.refresh();
            if (model.subDetails.isEmpty) {
              initPSPSubscriptionDetails(model, 1);
            }
          },
        );
      },
    );
  }
}

I'm sorry if it's kind of long, just want to show the whole file so nothing is out of context. As shown in the above code, i am using PagedSliverList like you suggested and yet the listener keeps querying the next page even before the user scrolls.

caiombueno commented 10 months ago

Hi @Israel001. From what I can see, you are using a sliver PagedSliverList within a Column.

Flutter follows two protocols for drawing widgets: Sliver and Box protocols. A Column expects its children to adhere to the Box protocol. However, in the case of a PagedSliverList, it uses the Sliver protocol.

If you want to use a paged list inside a Column, I would recommend using a PagedListView wrapped in an Expanded widget (to avoid an unbounded height), and make sure to turn off shrinkWrap.

Let me know if it doesn't work.

Israel001 commented 10 months ago

@caiombueno God bless you bro ❤️

It works!!!

Turning off the shrinkWrap is what did it for me 'cause I was already using Column and Expanded before but shrinkWrap was on.

Thank you very much bro.

I hope the original issue poster will also get his issue resolved.

Israel001 commented 10 months ago

@caiombueno Hello,

Sorry to disturb you again (:

After replicating the pagination logic as you explained for another scenario, it didn't work. I am getting the same issue as before, where the new pages are fetched before i even scroll.

I am using column inside a IndexedStack Widget. Here is an overview of my widget flow from scaffold to the pagination widget:

Scaffold -> IndexedStack -> Column -> Expanded -> Padding -> PagedListView -> Container -> Column

The Scaffold and IndexedStack is in a different file which imports the 1st Column Widget from another file.

Here is the code for the file that contains the PagedListView widget:

class CollectionHistoryViewState extends State<CollectionHistoryView> {
  final PagingController<int, UpcomingCollectionRequest>
      _upcomingCollectionsController = PagingController(firstPageKey: 1);
  final PagingController<int, CollectionRequest> _collectionsTodayController =
      PagingController(firstPageKey: 1);

  @override
  void dispose() {
    _upcomingCollectionsController.dispose();
    _collectionsTodayController.dispose();
    super.dispose();
  }

  fetchUpcomingCollections(Pagination pagination, int nextPageKey) async {
    try {
      String? accessToken = await getIt<StorageService>().getFromDisk(
        Constants.accessToken,
      );
      Response response = await getIt<Dio>().get(
        ServerRoutes.getUpcomingCollections(pagination),
        options: Options(
          headers: {"Authorization": "Bearer $accessToken"},
        ),
      );
      bool isLastPage = response.data['pagination']['page'] ==
          response.data['pagination']['pages'];
      List<UpcomingCollectionRequest> upcomingCollections =
          (response.data['data'] as List)
              .map((value) => UpcomingCollectionRequest.fromJson(value))
              .toList();
      if (isLastPage) {
        _upcomingCollectionsController.appendLastPage(upcomingCollections);
      } else {
        _upcomingCollectionsController.appendPage(
          upcomingCollections,
          nextPageKey,
        );
      }
    } on DioException catch (e) {
      if (context.mounted) handleDioException(e, context);
    }
  }

  fetchCollectionsToday(Pagination pagination, int nextPageKey) async {
    try {
      String? accessToken = await getIt<StorageService>().getFromDisk(
        Constants.accessToken,
      );
      Response response = await getIt<Dio>().get(
        ServerRoutes.getCollectionsToday(pagination),
        options: Options(
          headers: {"Authorization": "Bearer $accessToken"},
        ),
      );
      bool isLastPage = response.data['pagination']['page'] ==
          response.data['pagination']['pages'];
      List<CollectionRequest> collectionsToday = (response.data['data'] as List)
          .map((value) => CollectionRequest.fromJson(value))
          .toList();
      if (isLastPage) {
        _collectionsTodayController.appendLastPage(collectionsToday);
      } else {
        _collectionsTodayController.appendPage(
          collectionsToday,
          nextPageKey,
        );
      }
    } on DioException catch (e) {
      if (context.mounted) handleDioException(e, context);
    }
  }

  @override
  void initState() {
    widget.totalUpcomingCollections > widget.upcomingCollections.length
        ? _upcomingCollectionsController.appendPage(
            widget.upcomingCollections,
            2,
          )
        : _upcomingCollectionsController
            .appendLastPage(widget.upcomingCollections);

    widget.totalCollectionsToday > widget.collectionsToday.length
        ? _collectionsTodayController.appendPage(widget.collectionsToday, 2)
        : _collectionsTodayController.appendLastPage(widget.collectionsToday);

    _collectionsTodayController.addPageRequestListener((pageKey) {
      fetchCollectionsToday(Pagination(limit: 5, page: pageKey), pageKey);
    });
    _upcomingCollectionsController.addPageRequestListener((pageKey) {
      print("here???");
      fetchUpcomingCollections(Pagination(limit: 5, page: pageKey), pageKey);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final model = context.watch<ProfileViewModel>();

    int listCount = model.collectionState == "Collections"
        ? model.collectionState2 == 'Collections Today'
            ? model.collectionsToday.length
            : model.upcomingCollectionReqs.length
        : 0;
    String listInUse = "";
    String collectionListInUse =
        model.collectionState2 == 'Collections Today' ? "list" : "upcomingList";
    Map baseList = {};
    if (listCount < 1) {
      switch (model.collectionState) {
        case "Collections":
          listCount = model.collectionState2 == 'Collections Today'
              ? model.collectionsToday.length
              : model.upcomingCollectionReqs.length;
          collectionListInUse = model.collectionState2 == 'Collections Today'
              ? "list"
              : "upcomingList";
          break;
        case "Missed":
          listCount = model.collectionState2 == 'This Week'
              ? model.missedCollectionReqs.length
              : model.missedCollectionHistory.entries.length;
          listInUse = model.collectionState2 == 'This Week'
              ? "missedThisWeek"
              : "historyList";
          if (listInUse == 'historyList') {
            baseList = model.missedCollectionHistory;
          }
          break;
        case "Completed":
          listCount = model.collectionState2 == 'This Week'
              ? model.completedCollectionThisWeek.entries.length
              : model.completedCollectionHistory.entries.length;
          baseList = model.collectionState2 == "This Week"
              ? model.completedCollectionThisWeek
              : model.completedCollectionHistory;
          break;
        case "Unavailable":
          listCount = model.collectionState2 == 'This Week'
              ? model.unavailableCollectionThisWeek.entries.length
              : model.unavailableCollectionHistory.entries.length;
          baseList = model.collectionState2 == "This Week"
              ? model.unavailableCollectionThisWeek
              : model.unavailableCollectionHistory;
          break;
      }
    }

    Widget renderUpcomingCollections() {
      return PagedListView<int, UpcomingCollectionRequest>(
        shrinkWrap: false,
        pagingController: _upcomingCollectionsController,
        builderDelegate: PagedChildBuilderDelegate<UpcomingCollectionRequest>(
          itemBuilder: (context, item, index) {
            return CollectionCard(
              date: item.date!,
              areaOfOperation: item.street!['areaOfOperation']['name'],
              street:
                  '${item.street!['name']}, ${item.street!['area']}, ${item.street!['lga']['name']}, ${item.street!['state']['name']}',
              streetId: item.street!['id'],
              status: "missed",
              daysLeft: item.daysLeft,
            );
          },
        ),
      );
    }

    Widget renderCollectionsToday() {
      return PagedListView<int, CollectionRequest>(
        shrinkWrap: false,
        pagingController: _collectionsTodayController,
        builderDelegate: PagedChildBuilderDelegate<CollectionRequest>(
          itemBuilder: (context, item, index) {
            return CollectionCard(
              date: item.createdAt!,
              areaOfOperation: item.adminStreet!['areaOfOperation']['name'],
              street:
                  '${item.adminStreet!['name']}, ${item.adminStreet!['area']}, ${item.adminStreet!['lga']['name']}, ${item.adminStreet!['state']['name']}',
              streetId: item.id!,
              status: "missed",
            );
          },
        ),
      );
    }

    Widget renderWidget() {
      switch (model.collectionState) {
        default:
          if (collectionListInUse == "upcomingList") {
            return renderUpcomingCollections();
          }
          return renderCollectionsToday();
      }
    }

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Container(
          width: 100.w,
          decoration: BoxDecoration(
            color: const Color(0xffE9EBFF),
            borderRadius: BorderRadius.all(
              Radius.circular(10.sp),
            ),
          ),
          child: WasteNgLinearTab(
            tabItems: const [
              'Collections',
              'Missed',
              'Completed',
              'Unavailable',
            ],
            onItemSelected: (i, s) {
              model.collectionState = s;
              model.collectionState2 =
                  s != 'Collections' ? 'This Week' : 'Collections Today';
              switch (s) {
                case 'Collections':
                  // model.collectionStatusCount = model.collectionsToday.length;
                  break;
                case 'Missed':
                  // to be revisited
                  // model.collectionStatusCount =
                  //     model.missedCollectionReqs.length;
                  break;
              }
            },
            value: model.collectionState,
          ).paddingSymmetric(horizontal: 0.w, vertical: 2.h),
        ).paddingSymmetric(horizontal: 5.w, vertical: 0.h),
        WasteNgMaterialTab(
          statusCount: const <int?>[null, null],
          tabItems: model.collectionState == 'Missed' ||
                  model.collectionState == 'Completed' ||
                  model.collectionState == 'Unavailable'
              ? const ['This Week', 'History']
              : const ['Collections Today', 'Upcoming Collections'],
          onItemSelected: (i, s) {
            model.collectionState2 = s;
          },
          value: model.collectionState2,
        ).paddingSymmetric(horizontal: 5.w, vertical: 1.h).paddingTop((1.5).h),
        Expanded(
          flex: 1,
          child: listCount < 1
              ? const Center(child: Text("No Collection Requests"))
              : renderWidget().paddingSymmetric(
                  horizontal: 5.w,
                  vertical: 0.h,
                ),
        ),
      ],
    );
  }
}

Thanks in advance ❤️

caiombueno commented 8 months ago

hi @Israel001, sorry for the delayed response. Did you manage to solve it?