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

duplicate refresh requests when using controller on multiple views #148

Open
clragon opened this issue Nov 22, 2021 · 24 comments
Open

duplicate refresh requests when using controller on multiple views #148

clragon opened this issue Nov 22, 2021 · 24 comments
Labels
bug Something isn't working

Comments

@clragon
Copy link
Collaborator

clragon commented Nov 22, 2021

The information in this comment are outdated.
Please see the comments further down for a comprehensive issue description.


I have run into the curious issue that any PagedView that receives a refresh call
but isnt the top most route will call the requestPage callback twice in a row.

I have laced my code with extensive debug logs because it was impossible to breakpoint it.

heres what the log looks like:

#5c79c is the very first (root) route.
#6d0c2 is the middle route.
#8a43b is the top (current) route.

Performing hot reload...
Syncing files to device Windows...
Reloaded 145 of 1439 libraries in 2’413ms.
flutter: [#5c79c] instantiated!
flutter: [#5c79c] started waiting for a request page!
flutter: [#5c79c] loading page 1
flutter: [#5c79c] appending a new page of length 70
flutter: [#5c79c] providing a next page key: 2
flutter: [#5c79c] done requesting and adding page, releasing lock!
flutter: [#6d0c2] instantiated!
flutter: [#6d0c2] started waiting for a request page!
flutter: [#6d0c2] loading page 1
flutter: [#6d0c2] appending a new page of length 73
flutter: [#6d0c2] providing a next page key: 2
flutter: [#6d0c2] done requesting and adding page, releasing lock!
flutter: [#8a43b] instantiated!
flutter: [#8a43b] started waiting for a request page!
flutter: [#8a43b] loading page 1
flutter: [#8a43b] appending a new page of length 70
flutter: [#8a43b] providing a next page key: 2
flutter: [#8a43b] done requesting and adding page, releasing lock!
flutter: [#5c79c] triggered refresh by ValueNotifier<String>#515e8
flutter: [#5c79c] allowed a refresh!
flutter: [#6d0c2] triggered refresh by ValueNotifier<String>#515e8
flutter: [#6d0c2] allowed a refresh!
flutter: [#8a43b] triggered refresh by ValueNotifier<String>#515e8
flutter: [#8a43b] allowed a refresh!
flutter: [#5c79c] resetting posts now!
flutter: [#5c79c] allowed a refresh!
flutter: [#6d0c2] resetting posts now!
flutter: [#6d0c2] allowed a refresh!
flutter: [#8a43b] resetting posts now!
flutter: [#8a43b] allowed a refresh!
flutter: [#5c79c] calling super.refresh!
flutter: [#5c79c] started waiting for a request page!
flutter: [#5c79c] started waiting for a request page!
flutter: [#6d0c2] calling super.refresh!
flutter: [#6d0c2] started waiting for a request page!
flutter: [#6d0c2] started waiting for a request page!
flutter: [#8a43b] calling super.refresh!
flutter: [#8a43b] started waiting for a request page!
flutter: [#5c79c] loading page 1
flutter: [#6d0c2] loading page 1
flutter: [#8a43b] loading page 1
flutter: [#6d0c2] appending a new page of length 7
flutter: [#6d0c2] providing a next page key: 2
flutter: [#6d0c2] done requesting and adding page, releasing lock!
flutter: [#6d0c2] loading page 1
flutter: [#6d0c2] started waiting for a request page!
flutter: [#5c79c] appending a new page of length 79
flutter: [#5c79c] providing a next page key: 2
flutter: [#5c79c] done requesting and adding page, releasing lock!
flutter: [#5c79c] loading page 1
flutter: [#8a43b] appending a new page of length 80
flutter: [#8a43b] providing a next page key: 2
flutter: [#8a43b] done requesting and adding page, releasing lock!
flutter: [#6d0c2] appending a new page of length 7
flutter: [#6d0c2] providing a next page key: 2
flutter: [#6d0c2] done requesting and adding page, releasing lock!
flutter: [#6d0c2] loading page 2
flutter: [#6d0c2] started waiting for a request page!
flutter: [#6d0c2] done requesting and adding page, releasing lock!
flutter: [#6d0c2] loading page 2
flutter: [#5c79c] appending a new page of length 79
flutter: [#5c79c] providing a next page key: 2
flutter: [#5c79c] done requesting and adding page, releasing lock!
flutter: [#6d0c2] done requesting and adding page, releasing lock!
flutter: [#8a43b] banished to the shadow realm!
flutter: [#6d0c2] banished to the shadow realm!

in the log you can see that both routes that are not at the top, #5c79c and #6d0c2 get two requests for loading a new page.
said two requests are issued exactly at the same time.
In my code I use mutex locks to prevent multiple requests from ever running at the same time,
so those 2 requests are ran after each other, but theyre still ran with the same page key.

I have also printed the stack trace logs and made sure that it is not my code that is triggering multiple refresh requests.
In the log above, you can see how my linked valuenotifier is only ever triggering a single request.

You can also see how the top most route, #8a43b only triggers a single refresh request.

this issue also occurs on android and it happens exactly the same everytime.
I have also tested this with only a single route at the bottom, said route will still receive two refresh requests.

I am using version 4.0.0 on 268945f (because I would like to use the staggered grid and page view paged variants).

My code for using the PagingController is rather complicated but I doubt it is the cause of this issue,
regardless, it could be found here:

@clragon
Copy link
Collaborator Author

clragon commented Nov 23, 2021

okay, I have understood the nature of this issue better with some investigation.
What happens is that when multiple paged views are hooked up to the same paged controller, and a refresh is called on that controller, all of the pageviews will request a refresh.

The reason this happened in my code is because I replaced my normal page view with a paged page view.
This would lead to the following constelation: (single layer)

PagedGridView (controller a)
PagedPageView (controller a)
PagedGridView (controller b)

even though my code will immediately pop the pagedpageview in the case of it changing its contents to not include the item on the current page of the pageview, the pageview still seems to fire off a refresh before being popped, leading to multiple (2) refresh requests with the very same key.

I assume this "issue" would also be present in the previous version of this package, 3.1.0,
it just only happened to me because I used the pagedpageviews, only present in 4.0.0.

I will remove the pagedpageviews for now, though I am unsure how I could fix this issue on my end, or how it could be fixed in general. I assume, the paged controller would need to somehow dedupe these requests for my setup to work.

I am very sorry, all of this relevant information wasnt in my issue at first.

@clragon clragon changed the title double page request for routes that arent on top duplicate refresh requests when using controller on multiple views Nov 23, 2021
@clragon
Copy link
Collaborator Author

clragon commented Nov 27, 2021

I think my usage of the PagingController and the base structure of the package are clashing.

In my extension of the PagingController, a single item providing callback has to be specified and it is guarded by mutex locks, where as the normal PagingController barely takes control of the callback, as you may add as many listeners as you like and the controller doesnt keep track of whether it is completed or not.

But personally i do not understand how adding multiple listeners would work well, as the PagingStatus doesnt seem to factor in whether a request is currently happing or not.
If I read the source code right, it simply doesnt. the loading indicator is always there as long as there is a next page key to be processed.

Though its also unclear to me how having multiple page listeners would work in terms of order. If one of my 2 page listeners on page 3 (assuming I use integers as keys) takes longer and the user starts requesting page 4 and the first one from page 4 completes before the second on page 3, then the order is messed up.

Therefore, I would advocate for removing the ability to add multiple page request listeners and then have that one listener be more controlled so that it can be assured that not multiple requests are ongoing at the same time.

This would definitely be a rather large breaking change, but I feel like this behaviour would be more desirable than the current one.

@a7md0
Copy link

a7md0 commented Dec 13, 2021

I noticed the same behavior too, while using the Paging Controller to display in two views and manually refreshing the page.

My use case is initial results will be showed in horizontal scroll view (paginated), then an option to show all results in a grid view. Once the user refresh the results in the grid view, the current page get duplicated.

version 3 of this package

infinite_scroll_pagination: ^3.1.0

Flutter version

>flutter --version
Flutter 2.8.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision cf44000065 (5 days ago) • 2021-12-08 14:06:50 -0800
Engine • revision 40a99c5951
Tools • Dart 2.15.0

@karimSalahx
Copy link

karimSalahx commented Jan 13, 2022

I had the same problem don't know the solution but as a workaround I made a function that detects if the list is refreshed twice by identifying if the list contains duplicate items you can use a set or any other solution will make it also this is the code

bool _listIntersects<T>(
    PagingController<int, T> pagingController,
    List<T> data,
  ) {
    if (pagingController.itemList != null) {
      bool isIntersect = true;
      data.forEach(
        (item) {
          if ((pagingController.itemList!.contains(item))) isIntersect = false;
        },
      );
      return isIntersect;
    }
    return false;
  }

you can use it before appending either a page or last page to the paging controller

@clragon
Copy link
Collaborator Author

clragon commented Jan 13, 2022

thats not a very nice solution because this will lead to 2 API calls, which means more network traffic.

@karimSalahx
Copy link

thats not a very nice solution because this will lead to 2 API calls, which means more network traffic.

I know but it is better than making 2 API calls and update the state with duplicate data, if you found a better solution please mention me

@Nazarii77
Copy link

Nazarii77 commented Feb 14, 2022

Block the loading completely if some page is not loaded 100%! If the page size is small like 3, the page 0 and 1 are loading almost simultaniously on big screens. Original counter messes up and sometimes I see 1 page before page 0. Also there is need to block refresh if we currently loading some big page, like 10 items of huge json`s

//in class declaration
bool _isLoadingStarted = false;

//in init
@OverRide
void initState() {
super.initState();
_pagingController.addPageRequestListener((pageKey) async {
if ((_pagingController.value.status == PagingStatus.loadingFirstPage ||
_pagingController.value.status == PagingStatus.ongoing) &&
_isLoadingStarted) return;
_isLoadingStarted = true;
await _fetchPage(Provider.of(context, listen: false),
pageKey, widget.YourItemId);
});
}

//on refresh

onRefresh: () async {
if ((_pagingController.value.status ==
PagingStatus.loadingFirstPage ||
_pagingController.value.status == PagingStatus.ongoing) &&
_isLoadingStarted) return;
extensions.clear();
_pagingController.refresh();
},

@clragon
Copy link
Collaborator Author

clragon commented Mar 7, 2022

this issue can be traced back to exactly this place in the code:

line 160-172 in paged_layout_builder.dart

        listener: () {
          final status = _pagingController.value.status;

          if (status == PagingStatus.loadingFirstPage) {
            _pagingController.notifyPageRequestListeners(
              _pagingController.firstPageKey,
            );
          }

          if (status == PagingStatus.ongoing) {
            _hasRequestedNextPage = false;
          }
        },

Which makes the Paged View request a new page when the current state is loading first page.
This makes sense if there is only a singular view, but will immediately lead to duplicate requests if the controller is connected to multiple views.

This would need to be handled inside the controller instead, but then we would loose loading data only when the screen is built.

In the end, I suppose, it would still make sense if the controller deduped requests.

(I would also still argue for passing a callback to the controller that returns a Future and having the controller tightly control that callback, instead of adding pagelisteners)

@clragon
Copy link
Collaborator Author

clragon commented Mar 13, 2022

fun news, I found out that there are even more unrelated ways that the view can trigger duplicated requests.
I have absolutely no idea how it happens, but it can happen, very rarely.

Logs
-------------------------------------------------
[#2a6c2] 
#0      RawDataController.loadPage (package:e1547/interface/data/controller.dart:137)
#1      PagingController.notifyPageRequestListeners.<anonymous closure> (package:infinite_scroll_pagination/src/core/paging_controller.dart:215)
#2      _GrowableList.forEach (dart:core-patch/growable_array.dart:433)
#3      PagingController.notifyPageRequestListeners (package:infinite_scroll_pagination/src/core/paging_controller.dart:213)
#4      _PagedLayoutBuilderState.build.<anonymous closure> (package:infinite_scroll_pagination/src/widgets/helpers/paged_layout_builder.dart:164)
#5      _ListenableListenerState._handleChange (package:infinite_scroll_pagination/src/utils/listenable_listener.dart:53)
#6      _ListenableListenerState.initState (package:infinite_scroll_pagination/src/utils/listenable_listener.dart:34)
#7      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4893)
#8      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4729)
-------------------------------------------------
[#2a6c2] loadpage with 1
[#2a6c2] acquired mutex on page 1
[#2a6c2] done with loading page 1
-------------------------------------------------
[#572ad] 
#0      RawDataController.loadPage (package:e1547/interface/data/controller.dart:137)
#1      PagingController.notifyPageRequestListeners.<anonymous closure> (package:infinite_scroll_pagination/src/core/paging_controller.dart:215)
#2      _GrowableList.forEach (dart:core-patch/growable_array.dart:433)
#3      PagingController.notifyPageRequestListeners (package:infinite_scroll_pagination/src/core/paging_controller.dart:213)
#4      _PagedLayoutBuilderState._buildListItemWidget.<anonymous closure> (package:infinite_scroll_pagination/src/widgets/helpers/paged_layout_builder.dart:276)
#5      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144)
#6      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1089)
#7      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995)
#8      _rootRun (dart:async/zone.dart:1426)
-------------------------------------------------
[#572ad] loadpage with 2
[#572ad] acquired mutex on page 2
-------------------------------------------------
[#79f71] 
#0      RawDataController.loadPage (package:e1547/interface/data/controller.dart:137)
#1      PagingController.notifyPageRequestListeners.<anonymous closure> (package:infinite_scroll_pagination/src/core/paging_controller.dart:215)
#2      _GrowableList.forEach (dart:core-patch/growable_array.dart:433)
#3      PagingController.notifyPageRequestListeners (package:infinite_scroll_pagination/src/core/paging_controller.dart:213)
#4      _PagedLayoutBuilderState._buildListItemWidget.<anonymous closure> (package:infinite_scroll_pagination/src/widgets/helpers/paged_layout_builder.dart:276)
#5      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144)
#6      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1089)
#7      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995)
#8      _rootRun (dart:async/zone.dart:1426)
-------------------------------------------------
[#79f71] loadpage with 2
-------------------------------------------------
[#133c6] 
#0      RawDataController.loadPage (package:e1547/interface/data/controller.dart:137)
#1      PagingController.notifyPageRequestListeners.<anonymous closure> (package:infinite_scroll_pagination/src/core/paging_controller.dart:215)
#2      _GrowableList.forEach (dart:core-patch/growable_array.dart:433)
#3      PagingController.notifyPageRequestListeners (package:infinite_scroll_pagination/src/core/paging_controller.dart:213)
#4      _PagedLayoutBuilderState._buildListItemWidget.<anonymous closure> (package:infinite_scroll_pagination/src/widgets/helpers/paged_layout_builder.dart:276)
#5      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144)
#6      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1089)
#7      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995)
#8      _rootRun (dart:async/zone.dart:1426)
-------------------------------------------------
[#133c6] loadpage with 2
[#572ad] done with loading page 2
[#79f71] acquired mutex on page 2
[#79f71] done with loading page 2
[#133c6] acquired mutex on page 2
[#133c6] done with loading page 2
-------------------------------------------------
[#ce439] 
#0      RawDataController.loadPage (package:e1547/interface/data/controller.dart:137)
#1      PagingController.notifyPageRequestListeners.<anonymous closure> (package:infinite_scroll_pagination/src/core/paging_controller.dart:215)
#2      _GrowableList.forEach (dart:core-patch/growable_array.dart:433)
#3      PagingController.notifyPageRequestListeners (package:infinite_scroll_pagination/src/core/paging_controller.dart:213)
#4      _PagedLayoutBuilderState._buildListItemWidget.<anonymous closure> (package:infinite_scroll_pagination/src/widgets/helpers/paged_layout_builder.dart:276)
#5      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144)
#6      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1089)
#7      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995)
#8      _rootRun (dart:async/zone.dart:1426)
-------------------------------------------------
[#ce439] loadpage with 3
[#ce439] acquired mutex on page 3
[#ce439] done with loading page 3

as you can see, the PagedLayout triggered a request for page 2 three times in a row.
personally I have encountered this issue only 2 times and a user of mine has encountered it once.

There is nothing I can do in my own class that expands the PagingController to prevent this.
Mutexes can prevent having multiple requests run at once, but the PagedLayout can request the same key in quick succession.

Technically, I could keep track of which keys were requested already, or which key is being requested at this very moment, but I would like to stop trying to work around and on top of the package.

@ataknakbulut
Copy link

I have same issues. Im surprised there is really few peoples come here.

@ahetawal-p
Copy link

I am also seeing the same issue. I have a search bar, and a grid view with results. For the first search action, I see only single call being made by the listener.
But on trying a new search ->
I call

pagingController.refresh();
pagingController.notifyPageRequestListeners(0);

And thats when I see 2 requests being made with the same query..

@clragon
Copy link
Collaborator Author

clragon commented Apr 22, 2022

@ahetawal-p that's because you aren't supposed to notify the page request listeners after calling refresh, it already does that itself.

@ahetawal-p
Copy link

Thanks @clragon do you mean, after calling pagingController.refresh(); I can expect the pagingController to make a new call to the api for the new search query ?

@FarhanSajid1
Copy link

I see duplicates whenever I implement this in a search bar

@clragon
Copy link
Collaborator Author

clragon commented May 28, 2022

here's a fun workaround:
if you generate all your models with freezed,
they will now be equal if their fields are equal.

you now just have to toSet().toList() on all your data everytime you add any,
and you will never see duplicates.

This is a terrible solution.

@FarhanSajid1
Copy link

@clragon hey, I've been facing the same issue, I think there are a lot of ways to go about this. The simplest might be to set a variable to track the last refreshed key and ignore the reload if that key is passed in again?

initialize

 int lastRefreshedPage = -1;
  final PagingController<int, widget>
      controller = PagingController(firstPageKey: 0);

refresh call

                             onRefresh: () async {
                                controller.lastRefreshedPage = -1;
                                controller.controller
                                    .refresh();
                                if (kDebugMode) {
                                  print('finished refreshing');
                                }
                              },

mutex call

  @override
  void onInit() {
    super.onInit();
    controller.addPageRequestListener((pageKey) async {
      if (!await canRefresh()) {
        return;
      }
      await paginatedFunction(pageKey);
    });
  }

  // locking function
  Future<bool> canRefresh() async {
    if (_requestLock.isLocked) {
      if (_isRefreshing) {
        return false;
      }
      // waits to acquire lock
      await _requestLock.acquire();
      if (kDebugMode) {
        print('releasing lock');
      }
      return true;
    } else {
      return true;
    }
  }

Function call

  Future paginatedFunction(int after) async {
    // we've seen the requested key before, just skip
    if (lastRefreshedPage == after) {
      print('I saw this key before $after skipping call');
      return;
    }
.... 
logic

@FarhanSajid1
Copy link

@clragon hey, I've been facing the same issue, I think there are a lot of ways to go about this. The simplest might be to set a variable to track the last refreshed key and ignore the reload if that key is passed in again?

initialize

 int lastRefreshedPage = -1;
  final PagingController<int, widget>
      controller = PagingController(firstPageKey: 0);

refresh call

                             onRefresh: () async {
                                controller.lastRefreshedPage = -1;
                                controller.controller
                                    .refresh();
                                if (kDebugMode) {
                                  print('finished refreshing');
                                }
                              },

mutex call

  @override
  void onInit() {
    super.onInit();
    controller.addPageRequestListener((pageKey) async {
      if (!await canRefresh()) {
        return;
      }
      await paginatedFunction(pageKey);
    });
  }

  // locking function
  Future<bool> canRefresh() async {
    if (_requestLock.isLocked) {
      if (_isRefreshing) {
        return false;
      }
      // waits to acquire lock
      await _requestLock.acquire();
      if (kDebugMode) {
        print('releasing lock');
      }
      return true;
    } else {
      return true;
    }
  }

Function call

  Future paginatedFunction(int after) async {
    // we've seen the requested key before, just skip
    if (lastRefreshedPage == after) {
      print('I saw this key before $after skipping call');
      return;
    }
.... 
logic

So this seems to not work in production for some reason

@rafaelmaia8384
Copy link

Any update on this?

@rafaelmaia8384
Copy link

rafaelmaia8384 commented Mar 2, 2023

So I was able to fix this in my case using AutomaticKeepAliveClientMixin

@override
  void initState() {
    widget.moduleController.pagingController
        .addPageRequestListener(_requestListener);
    super.initState();
  }

The initState was duplicating the request listeners.

@clragon
Copy link
Collaborator Author

clragon commented Mar 3, 2023

@FarhanSajid1 thats because you copied code from my repo without understanding it.
You somehow removed the part where I unlock the mutex again after waiting for it.

All of that code is old now and I use something different.
I have a very complicated mutex object that keeps track of what pages have been requested by the controller, plus my custom controller which keeps a tight grip on all requests and also knows what function is used to get new data. basically I have moved all functionality into the controller instead of it coming from outside. But I believe my additions might be too complicated.
This issue could easier be solved if the controller was structured a bit more like a Bloc, where there is a stream of page requests and the stream is consumed one by one, each request can then be a request for a page, or it can be a request for a reset of the controller, etc. That way, the state of the controller is clearly managed and the duplicate page problem could be counteracted.

But I havent come up with a clear idea of how that would look like.

My current controller looks like this:
https://github.com/clragon/e1547/blob/df877eb81fd49c0be3151de7328301b81d445411/lib/interface/data/controller.dart#LL48C10-L48C10
Where loadPage is essentially the function that would consume the stream events.
Its instead just an async function that handles all state changes, more or less, while being locked by a mutex.
it gets a page, whether we reset the controller state, whether we reset the state in the background (meaning, we load new data first) and whether we ignore cache. I have also overriden the refresh function to just call this to eliminate all race conditions.
You can also see how loadPage calls requestPage, which you have to override to make this controller functional.
Later down the line my specific controllers then sometimes allow passing in a callback that is called inside requestPage.
The controller also has a bunch of other functions like success and failure or reset which are only for my specific usecases.
I think the most important thing is that state changes and the function that loads new data are controlled by the controller tightly instead of being controlled by the user.

@syahrezafauzi
Copy link

same issue,
I using Getx, I have bottom navigation and a Navigator widget nested with 2 page.
each page have one view (PagedListView).

the issue appear when switching 2 page, the listener called multiple times (duplicate) with undesirable pageKey.
the listener calls still increase when switch the pages again.
when working on one page (without switching page), works perfectly.

I think the issue relate with this package.
I can't figure out what's wrong more, any help?

@brunodmn
Copy link

I have similar problem using getx..even though last page is reached, fetchPage() is called more than once sometimes (on route init).

This is my controler
 class ContractsController extends GetxController {
  int limit = 30;
  final PagingController<int, SubscriberContract> pagingController =
      PagingController(firstPageKey: 1);

  @override
  void onInit() {
    pagingController.addPageRequestListener((pageKey) {
      _getContractsActive(page: pageKey, limit: limit);
    });
    super.onInit();
  }

  @override
  void onClose() {
    pagingController.dispose();
    super.onClose();
  }

  Future<void> _getContractsActive(
      {required int page, required int limit}) async {
    '_getContractsActive ran -  page: $page'.printInfo();
    final contractsOrFailure = await ApiRepository.to.getSubscriberContracts(
        situation: ContractSituation.active, page: page, limit: limit);
    contractsOrFailure.fold((l) {
      pagingController.error = l.message;
    }, (r) {
      final isLastPage = r.length < limit;
      if (isLastPage) {
        'last page reached'.printInfo();
        pagingController.appendLastPage(r);
      } else {
        final nextPageKey = page + 1;
        nextPageKey.printInfo();
        pagingController.appendPage(r, nextPageKey);
      }
    });
  }
}

My page is not nested nor is called from different views, I really would appreciate some analysis as I could not find out what is making the fetch being called sometimes once, sometimes 3 times on route initialization.

On bellow logs we can see that controller is initialized (where listener is added), _getContractsActive() is called 3 times, also the condition to append last page (making last page being added 3 times and presenting duplicated items on view) :

[GETX] GOING TO ROUTE /home/contratos
[GETX] Instance "ContractsController" has been created
[GETX] Info: String init...
[GETX] Instance "ContractsController" has been initialized
3 [GETX] Info: String _getContractsActive ran -  page: 1
2 [GETX] Info: String last page reached
[GETX] Info: String last page reached

My workaround was creating a variable to control function execution (very ugly):

final initialLoadingCalled = false.obs;
Future<void> _getContractsActive(
      {required int page, required int limit}) async {

   (...)

    if (page == 1) {
      if (initialLoadingCalled.value == true) {
        return;
      } else {
        initialLoadingCalled(true);
      }
    }

   (...)

  }

I even created a minimal sample using getx and this package, strange enough, it works ok on the sample.

@wildsurfer
Copy link

I'm using only one view and one controller but I also face this issue.

In my case, the build method of my page was called twice. Looks like this is expected Flutter behaviour.

@RedC4ke
Copy link

RedC4ke commented Oct 2, 2024

@karimSalahx You've got a mixup in your method, I've corrected it:

bool _listIntersects<T>(
    PagingController<int, T> pagingController,
    List<T> data,
  ) {
    if (pagingController.itemList != null) {
      bool isIntersect = false;
      data.forEach(
        (item) {
          if ((pagingController.itemList!.contains(item))) isIntersect = true;
        },
      );
      return isIntersect;
    }
    return false;
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests