Как я могу реализовать элементы ленивой загрузки для бесконечного списка? Я хочу загрузить больше товаров по сети, когда пользователь перейдет в конец списка.
Flutter ListView ленивая загрузка
Ответ 1
Вы можете слушать ScrollController
.
ScrollController
содержит некоторую полезную информацию, такую как scrolloffset и список ScrollPosition
.
В вашем случае интересная часть находится в controller.position
который является в настоящее время видимым ScrollPosition
. Который представляет сегмент прокручиваемого.
ScrollPosition
содержит информацию о его положении внутри прокручиваемого. Такие как extentBefore
и extentAfter
. Или это размер, со extentInside
.
Учитывая это, вы можете инициировать серверный вызов на основе extentAfter
который представляет оставшееся доступное пространство прокрутки.
Вот базовый пример, используя то, что я сказал.
class MyHome extends StatefulWidget {
@override
_MyHomeState createState() => new _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
ScrollController controller;
List<String> items = new List.generate(100, (index) => 'Hello $index');
@override
void initState() {
super.initState();
controller = new ScrollController()..addListener(_scrollListener);
}
@override
void dispose() {
controller.removeListener(_scrollListener);
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Scrollbar(
child: new ListView.builder(
controller: controller,
itemBuilder: (context, index) {
return new Text(items[index]);
},
itemCount: items.length,
),
),
);
}
void _scrollListener() {
print(controller.position.extentAfter);
if (controller.position.extentAfter < 500) {
setState(() {
items.addAll(new List.generate(42, (index) => 'Inserted $index'));
});
}
}
}
Вы можете ясно видеть, что при достижении конца прокрутки полоса прокрутки увеличивается из-за загрузки большего количества элементов.
Ответ 2
Спасибо за подход Rémi Rousselet, но он не решает всех проблем. Особенно, когда ListView прокручивается вниз, он по-прежнему вызывает scrollListener пару раз. Лучший подход - объединить слушателя уведомлений с подходом Remi. Вот мое решение:
bool _handleScrollNotification(ScrollNotification notification) {
if (notification is ScrollEndNotification) {
if (_controller.position.extentAfter == 0) {
loadMore();
}
}
return false;
}
@override
Widget build(BuildContext context) {
final Widget gridWithScrollNotification = NotificationListener<
ScrollNotification>(
onNotification: _handleScrollNotification,
child: GridView.count(
controller: _controller,
padding: EdgeInsets.all(4.0),
// Create a grid with 2 columns. If you change the scrollDirection to
// horizontal, this would produce 2 rows.
crossAxisCount: 2,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0,
// Generate 100 Widgets that display their index in the List
children: _documents.map((doc) {
return GridPhotoItem(
doc: doc,
);
}).toList()));
return new Scaffold(
key: _scaffoldKey,
body: RefreshIndicator(
onRefresh: _handleRefresh, child: gridWithScrollNotification));
}
Ответ 3
В решении используется ScrollController, и я увидел комментарии о странице.
Я хотел бы поделиться своими выводами о пакете incrementally_loading_listview https://github.com/MaikuB/incrementally_loading_listview.
Как сказано в упаковке: это может быть использовано для загрузки разбитых на страницы данных, полученных из запросов API.
В основном, когда ListView создает последний элемент, а это означает, что пользователь прокрутил вниз.
Надеюсь, что это может помочь кому-то, у кого есть подобные вопросы.
В целях демонстрации я изменил пример, чтобы позволить странице включать только один элемент и добавить CircularProgressIndicator.
...
bool _loadingMore;
bool _hasMoreItems;
int _maxItems = 30;
int _numItemsPage = 1;
...
_hasMoreItems = items.length < _maxItems;
...
return IncrementallyLoadingListView(
hasMore: () => _hasMoreItems,
itemCount: () => items.length,
loadMore: () async {
// can shorten to "loadMore: _loadMoreItems" but this syntax is used to demonstrate that
// functions with parameters can also be invoked if needed
await _loadMoreItems();
},
onLoadMore: () {
setState(() {
_loadingMore = true;
});
},
onLoadMoreFinished: () {
setState(() {
_loadingMore = false;
});
},
loadMoreOffsetFromBottom: 0,
itemBuilder: (context, index) {
final item = items[index];
if ((_loadingMore ?? false) && index == items.length - 1) {
return Column(
children: <Widget>[
ItemCard(item: item),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 60.0,
height: 60.0,
color: Colors.grey,
),
Padding(
padding: const EdgeInsets.fromLTRB(
8.0, 0.0, 0.0, 0.0),
child: Container(
color: Colors.grey,
child: Text(
item.name,
style: TextStyle(
color: Colors.transparent),
),
),
)
],
),
Padding(
padding: const EdgeInsets.fromLTRB(
0.0, 8.0, 0.0, 0.0),
child: Container(
color: Colors.grey,
child: Text(
item.message,
style: TextStyle(
color: Colors.transparent),
),
),
)
],
),
),
),
Center(child: CircularProgressIndicator())
],
);
}
return ItemCard(item: item);
},
);
полный пример https://github.com/MaikuB/incrementally_loading_listview/blob/master/example/lib/main.dart
Пакет использует ListView index = last item и loadMoreOffsetFromBottom, чтобы определить, когда загружать больше.
itemBuilder: (itemBuilderContext, index) {
if (!_loadingMore &&
index ==
widget.itemCount() -
widget.loadMoreOffsetFromBottom -
1 &&
widget.hasMore()) {
_loadingMore = true;
_loadingMoreSubject.add(true);
}
Ответ 4
Я использую https://pub.dev/packages/loadany для обработки загрузки больше, потому что он поддерживает ListView, GridView, ScrollView
Ответ 5
Используйте пакет lazy_load_scrollview: 1.0.0, который использует ту же концепцию за кулисами, на которые здесь ответил мир панд. Пакет облегчает реализацию.