Good use of FetchMore GQL for infinite scroll pagination ListView with Flutter
One problem here is that you're calling _scrollController.addListener()
within the build
function, so you are adding a listener every time the view rebuilds. This is why all the earlier fetchMore(opts)
calls are triggered every time maxScrollExtent
is reached.
You could move your addListener
call to a more appropriate lifecycle method like initState
, but I would actually recommend using a NotificationListener<ScrollUpdateNotification>
which is more declarative and may suite your current code better.
Unrelatedly, you might want to check for an empty "stop" cursor to prevent infinite empty last page requests (though maybe not if your list can grow and your backend accounts for it).
Comments
-
poka over 1 year
I'm trying to create a ListView of elements comming from a GraphQL API with graphql-flutter library.
The build of the first page is OK. Then when I start to scroll, the result will be good, my ListView show the second page, then third ect ... correctly.
The problem is, from the second page loading, there is a problem. FetchMore function seems to build multiple request at each page loading. It's like all previous page ever load are load again, more and more.
Strangely as I said, the result of the displayed list is correct for several pages, then suddenly the list loops back from a previous page, then resumes further ...
This is my Query widget:
Query( options: QueryOptions( document: gql(getHistory), variables: <String, dynamic>{ 'pubkey': this.pubkey, 'number': nRepositories, 'cursor': null }, ), builder: (QueryResult result, {refetch, FetchMore fetchMore}) { if (result.isLoading && result.data == null) { return const Center( child: CircularProgressIndicator(), ); } if (result.hasException) { return Text('\nErrors: \n ' + result.exception.toString()); } if (result.data == null && result.exception.toString() == null) { return const Text('Both data and errors are null'); } final List<dynamic> blockchainTX = (result.data['txsHistoryBc']['both']['edges'] as List<dynamic>); final Map pageInfo = result.data['txsHistoryBc']['both']['pageInfo']; final String fetchMoreCursor = pageInfo['endCursor']; FetchMoreOptions opts = FetchMoreOptions( variables: {'cursor': fetchMoreCursor}, updateQuery: (previousResultData, fetchMoreResultData) { final List<dynamic> repos = [ ...previousResultData['txsHistoryBc']['both']['edges'] as List<dynamic>, ...fetchMoreResultData['txsHistoryBc']['both']['edges'] as List<dynamic> ]; fetchMoreResultData['txsHistoryBc']['both']['edges'] = repos; return fetchMoreResultData; }, ); _scrollController ..addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { if (!result.isLoading) { print( "DEBUG fetchMoreCursor in scrollController: $fetchMoreCursor"); fetchMore(opts); } } }); print( "###### DEBUG Parse blockchainTX list. Cursor: $fetchMoreCursor ######"); List _transBC = parseHistory(blockchainTX); return Expanded( child: HistoryListView( scrollController: _scrollController, transBC: _transBC, historyData: result), ); }, ),
Then the Widget
HistoryListView
build the list fromtransBC
data. This is the print result ofDEBUG
lines (see the code above) for just one page loading, from the start of the app:I/flutter ( 8745): ###### DEBUG Parse blockchainTX list. Cursor: 386237:8A98F83A120EF89FC65CF43BEE77068F3DA5734340B7987FA059D582A06934F8 ###### I/flutter ( 8745): DEBUG fetchMoreCursor in scrollController: 386237:8A98F83A120EF89FC65CF43BEE77068F3DA5734340B7987FA059D582A06934F8 I/flutter ( 8745): ###### DEBUG Parse blockchainTX list. Cursor: 386237:8A98F83A120EF89FC65CF43BEE77068F3DA5734340B7987FA059D582A06934F8 ###### I/flutter ( 8745): DEBUG fetchMoreCursor in scrollController: 386237:8A98F83A120EF89FC65CF43BEE77068F3DA5734340B7987FA059D582A06934F8 I/flutter ( 8745): ###### DEBUG Parse blockchainTX list. Cursor: 386237:8A98F83A120EF89FC65CF43BEE77068F3DA5734340B7987FA059D582A06934F8 ###### I/flutter ( 8745): ###### DEBUG Parse blockchainTX list. Cursor: 384695:4BF72317A538FB37F71C0A8D5CC36F319F87B7421260F128EFFF75B9A17C2CC7 ###### I/flutter ( 8745): ###### DEBUG Parse blockchainTX list. Cursor: 384695:4BF72317A538FB37F71C0A8D5CC36F319F87B7421260F128EFFF75B9A17C2CC7 ######
Note that the
scrollController
function is executed twice, with the first cursor as the same value. And theparseHistory
function is then executed 3 times! I just scrolled to load 1 page here.I really don't understand this behavior ... I've been working on it for a week now, if developers of this library could explain it to me that would be fantastic.
I am using version 4.0.0-beta.6 of the
graphql_flutter
library.
Edit: This is the UI asociate for this test:
The value of
nRepository
here is3
.