下拉刷新
很简单,直接使用
RefreshIndicator
组件,onRefresh
为重新获取数据的方法
Widget build(BuildContext context) {return Scaffold(body: Container(padding: EdgeInsets.all(2.0),child: RefreshIndicator(onRefresh: _refresh,backgroundColor: Colors.blue,child: ListView.builder(itemCount: _dataList.length,itemBuilder: (context, index) {return ListItem(_dataList[index]);},),),),);}Future<Null> _refresh() async {_dataList.clear();await _loadFirstListData();return;}
复制代码
上拉加载更多
我们先看一下效果
- 让我们先从最原始的十条的数据开始
class MyHomePage extends StatefulWidget {@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {List<int> items = List.generate(10, (i) => i);@overridevoid initState() {super.initState();}@overridevoid dispose() {super.dispose();}@overrideWidget build(BuildContext context) {return new Scaffold(appBar: AppBar(title: Text("Infinite ListView"),),body: ListView.builder(itemCount: items.length,itemBuilder: (context, index) {return ListTile(title: new Text("Number $index"));},),);}
}
复制代码
- 现在我们要编写一个加载更多数据的方法,用来模拟
http
请求
Future<List<int>> fakeRequest(int from, int to) async {return Future.delayed(Duration(seconds: 2), () {return List.generate(to - from, (i) => i + from);});
}
复制代码
- 现在我们想要让用户将
ListView
滑动到最末端的触发fakeRequest
来加载更多数据,最简单的实现方式就是使用ScrollController
来完成,ScrollController
会监听滚动事件,当ListView
滚动到末端的时候他会发出一个请求。在这里还有一件需要注意的事就是为了避免对服务器不断地请求,我们需要做一个标记isPerformingRequest
只有当它为false
的时候才允许对后台进行请求。
class _MyHomePageState extends State<MyHomePage> {List<int> items = List.generate(10, (i) => i);ScrollController _scrollController = new ScrollController();bool isPerformingRequest = false;@overridevoid initState() {super.initState();_scrollController.addListener(() {if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {_getMoreData();}});}@overridevoid dispose() {_scrollController.dispose();super.dispose();}_getMoreData() async {if (!isPerformingRequest) {setState(() => isPerformingRequest = true);List<int> newEntries = await fakeRequest(items.length, items.length + 10);setState(() {items.addAll(newEntries);isPerformingRequest = false;});}}@overrideWidget build(BuildContext context) {return new Scaffold(appBar: AppBar(title: Text("Infinite ListView"),),body: ListView.builder(itemCount: items.length,itemBuilder: (context, index) {return ListTile(title: new Text("Number $index"));},controller: _scrollController,),);}
}
复制代码
如果你现在运行程序你将会看到我们的列表已经可以实现动态加载了,但是这距离我们的目标还很远,我们需要添加一些标志动作让用户这道请求已经开始。
- 接下来我们要用到
CircularProgressIndicator
去完成这个加载标志
Widget _buildProgressIndicator() {return new Padding(padding: const EdgeInsets.all(8.0),child: new Center(child: new Opacity(opacity: isPerformingRequest ? 1.0 : 0.0,child: new CircularProgressIndicator(),),),);
}
复制代码
- 现在我们将这个加载标志放到我们的
ListView
中去,注意这里要给itemCount
加出一块空间来放置我们的_buildProgressIndicator
@override
Widget build(BuildContext context) {return new Scaffold(appBar: AppBar(title: Text("Infinite ListView"),),body: ListView.builder(itemCount: items.length + 1,itemBuilder: (context, index) {if (index == items.length) {return _buildProgressIndicator();} else {return ListTile(title: new Text("Number $index"));}},controller: _scrollController,),);
}
复制代码
- 到这里加载更多数据的功能基本完成了,为了更加美观我们还要处理当没有请求到更多数据的时候动作,在这里我们添加一个动画没有更多数据的时候
ListView
向下移动覆盖正在加载更多数据的标志
_getMoreData() async {if (!isPerformingRequest) {setState(() => isPerformingRequest = true);List<int> newEntries = await fakeRequest(items.length, items.length); //returns empty listif (newEntries.isEmpty) {double edge = 50.0;double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels;if (offsetFromBottom < edge) {_scrollController.animateTo(_scrollController.offset - (edge -offsetFromBottom),duration: new Duration(milliseconds: 500),curve: Curves.easeOut);}}setState(() {items.addAll(newEntries);isPerformingRequest = false;});}
}
复制代码