I am building a Listview Builder in Flutter and populating the items via a provider which fetches the data from firestore in a paginated manner.
So the overall flow is -
- Main Widget gets access to items via Provider that returns a Future.
- Main Widget then creates a ListView Builder wrapped inside a FutureBuilder.
- The provider uses a service to query Firestore and gets an initial set of items via a getter
- The provider then calls getMoreItemsData() for getting the next set of items from the scroll listener when the user scrolls to the bottom of the list.
The issue I am facing is that after calling getMoreItemsData() my widget scroll position gets reset to the first item, whereas I want it to keep it at the last item so that the user can continue scrolling. I think it is happening due to the notifyListeners() that gets called from getMoreItemsData() but I need it to refresh my list of items. Is there a way to do this without resetting the scroll position to the top of the list?
Here is the Main widget with steps 1 and 2:
@override
Widget build(BuildContext context) {
final itemsData = Provider.of<itemsProvider>(context);
List<item> items;
ScrollController _scrollController = new ScrollController();
return FutureBuilder(
future: itemsData.items,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child:
CircularProgressIndicator(backgroundColor: Colors.red));
} else {
if (snapshot.hasError) {
// return: show error widget
}
_scrollController
..addListener(
() {
double maxScroll = _scrollController.position.maxScrollExtent;
double currentScroll = _scrollController.position.pixels;
double delta = MediaQuery.of(context).size.height * 0.30;
if (maxScroll - currentScroll <= delta) {
itemsData.getMoreitemsData();
}
},
);
items = snapshot.data ?? [];
return ListView.builder(
itemCount: items.length,
controller: _scrollController,
itemBuilder: (BuildContext context, int index) =>
Card(
margin: EdgeInsets.only(top: 10.0),
elevation: 4,
child: Text(items[index].caption),
// ... Card defined with some items
),
);
}
},
);
}
}
Here is my provider snippet with steps 3 and 4
class ItemsProvider with ChangeNotifier {
bool _getMoreData = false;
Future<List<Item>> get items async {
_items = await Service.getItems();
return _items;
}
void getMoreItemsData() {
_getMoreItems = true;
notifyListeners();
}
}
Here is my service snippet:
class Service {
static final firestoreInstance = Firestore.instance;
static DocumentSnapshot lastDocument, firstDocument;
static final _paginationNext = 3;
static List<Item> _itemData = [];
static Future getItems(bool getMoreData) async {
var dataFirestore;
if (!getMoreData) {
_itemData = [];
}
if (getMoreData) {
dataFirestore = await firestoreInstance
.collection("items")
.orderBy("sell", descending: true)
.startAfterDocument(lastDocument)
.limit(_paginationNext)
.getDocuments();
} else {
dataFirestore = await firestoreInstance
.collection("items")
.orderBy("sell", descending: true)
.limit(_paginationNext)
.getDocuments();
}
lastDocument =
dataFirestore.documents[dataFirestore.documents.length - 1];
dataFirestore.documents.forEach(
(doc) {
_itemData.add(Item.fromJson(doc.data));
},
);
return _itemData;
}
Here is a code snippet for Items model
class Item {
String item;
String sell;
Item({
@required this.item,
@required this.sell,
// More ...
});
factory Item.fromJson(Map<dynamic, dynamic> json) {
return new Item(
item: json['item'],
sell: json['sell'],
// More ...
);
}
}