下拉重新整理
很簡單,直接使用
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);
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget 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;
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
_getMoreData();
}
});
}
@override
void 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;
});
}
}
@override
Widget 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 list
if (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;
});
}
}
複製程式碼