我的用例是创建商品的列表视图(每个商品的外观相同,可能会有大量的商品,例如> 10000)。我尝试过 -具有ListView.builder的ListView:它仅在显示项目时才呈现该项目 -ScrollController:确定何时加载下一个项目(分页) -然后我使用List通过将http中的数据添加到List实例中来使用http存储从Restful API获取的数据
这种方法是可以的,但是如果用户继续滚动页面,则List实例将包含越来越多的项目,它可能会因堆栈溢出错误而崩溃。
如果我不调用List.addAll(),而是分配从api获取的数据,例如:list = data; 我有一个问题,当用户向上滚动时,他/她将无法看到以前的项目。
是否有解决此问题的好方法?谢谢!
import 'package:flutter/material.dart';
import 'package:app/model.dart';
import 'package:app/components/item.dart';
abstract class PostListPage extends StatefulWidget {
final String head;
DealListPage(this.head);
}
abstract class PostListPageState<T extends PostListPage> extends State<PostListPage> {
final int MAX_PAGE = 2;
DealListPageState(String head) {
this.head = head;
}
final ScrollController scrollController = new ScrollController();
void doInitialize() {
page = 0;
try {
list.clear();
fetchNextPage();
}
catch(e) {
print("Error: " + e.toString());
}
}
@override
void initState() {
super.initState();
this.fetchNextPage();
scrollController.addListener(() {
double maxScroll = scrollController.position.maxScrollExtent;
double currentScroll = scrollController.position.pixels;
double delta = 200.0; // or something else..
if ( maxScroll - currentScroll <= delta) {
fetchNextPage();
}
});
}
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
void mergeNewResult(List<PostListItem> result) {
list.addAll(result);
}
Future fetchNextPage() async {
if (!isLoading && mounted) {
page++;
setState(() {
isLoading = true;
});
final List<PostListItem> result = await doFetchData(page);
setState(() {
if (result != null && result.length > 0) {
mergeNewResult(result);
} else {
//TODO show notification
}
isLoading = false;
});
}
}
Future doFetchData(final int page);
String head;
List<PostListItem> list = new List();
var isLoading = false;
int page = 0;
int pageSize = 20;
final int scrollThreshold = 10;
Widget buildProgressIndicator() {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: isLoading ? 1.0 : 0.0,
child: new CircularProgressIndicator(),
),
),
);
}
@override
Widget build(BuildContext context) {
ListView listView = ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (BuildContext context, int index) {
if (index == list.length) {
return buildProgressIndicator();
}
if (index > 0) {
return Column(
children: [Divider(), PostListItem(list[index])]
);
}
return PostListItem(list[index]);
},
controller: scrollController,
itemCount: list.length
);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(head),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
},
),
// action button
IconButton(
icon: Icon(Icons.more_horiz),
onPressed: () {
},
),
]
),
body: new RefreshIndicator(
onRefresh: handleRefresh,
child: listView
),
);
}
Future<Null> handleRefresh() async {
doInitialize();
return null;
}
}
在我的情况下,当列表长度为600时,我开始出现堆栈溢出错误,如:
I/flutter ( 8842): Another exception was thrown: Stack Overflow
I/flutter ( 8842): Another exception was thrown: Stack Overflow
屏幕:
某种程度上,颤振不再显示该错误的更多细节。
答案 0 :(得分:0)
我为a related question about paginated scrolling写了一些示例代码,您可以查看这些代码。
我没有在此处实现缓存无效化,但是可以使用getPodcast
方法中的以下类似内容轻松地将其扩展:从当前位置删除所有索引超过100个的项目:
for (key in _cache.keys) {
if (abs(key - index) > 100) {
_cache.remove(key);
}
}
甚至更复杂的实现方式也可以考虑滚动速度和过去的用户行为,以绘制概率曲线(或更简单的高斯曲线)来更智能地获取内容。