Flutter开发者

ListView下拉刷新与加载更多

2018-05-03

上期回顾

在前面的文章中我们看了进度组件ProgressIndicator的用法,怎么样大家Get了吗?
那么我们今天就来看下ProgressIndicator的应用,一起来看下Flutter中的下拉刷新与加载更多是如何实现的。

下拉刷新

在Flutter中系统已经为我们提供了google material design的刷新效果,我们可以使用RefreshIndicator组件来实现Flutter中的下拉刷新,下面们还是先来看下如何使用吧

构造方法

1
2
3
4
5
6
7
8
9
10

const RefreshIndicator({
Key key,
@required this.child,
this.displacement: 40.0,下拉展示距离
@required this.onRefresh,刷新回调
this.color,//刷新进度颜色
this.backgroundColor,//背景颜色
this.notificationPredicate: defaultScrollNotificationPredicate,
})

那么我们还是结合ListView的使用来看下举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}

class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
List<int> items = List.generate(16, (i) => i);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Refresh"),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("Index$index"),
);
},
));
}
}

首先我们还是先来回顾下ListView的用法,我们使用ListView.builder来创建了一个ListView使用
List.generate()方法来创建了一个长度为16的List,并把List里的值赋值给ListView Item中的ListTile。

那么,我们在上面的基础上加上下拉刷新组件再来试下效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}

class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
List<int> items = List.generate(16, (i) => i);

Future<Null> _handleRefresh() async {
await Future.delayed(Duration(seconds: 5), () {
print('refresh');
setState(() {
items.clear();
items = List.generate(40, (i) => i);
return null;
});
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Refresh"),
),
body: new RefreshIndicator(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("Index$index"),
);
},
),
onRefresh: _handleRefresh,
),
);
}
}

在上面的代码中我们使用_handleRefresh()方法来处理下拉刷新的回调

1
2
3
4
5
6
7
8
9
10
Future<Null> _handleRefresh() async {
await Future.delayed(Duration(seconds: 5), () {
print('refresh');
setState(() {
items.clear();
items = List.generate(40, (i) => i);
return null;
});
});
}

其中 Future.delayed()方法可以选择延迟处理任务,这里我们假设网络的延迟是5秒,来看下效果。

可以看到,当我们下拉刷新结束后我们ListView的数据总数变成了40条。

接下来我们来修改下刷新进度的颜色与背景颜色再来看下效果。

当然,这个下拉刷新不是仅仅只能用在ListView中的,其他的组件都可以使用这个的。

下面我们就来介绍下如何实现ListView的上拉加载更多吧。

上拉加载更多

对于加载更多的组件在Flutter中是没有提供的,所以在这里我们就需要考虑如何实现的。

在ListView中有一个ScrollController属性,它就是专门来控制ListView滑动事件,在这里我们可以根据ListView的位置来判断是否滑动到了底部来做加载更多的处理。

在这里我们可以使用如下代码来判断ListView 是否华东到了底部

1
2
3
4
5
6
7
8

_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print("loadMore");

}
});

_scrollController是我们初始化的ScrollController对象,通过监听我们可以判断现在的位置是否是最大的下滑位置来判断是否下滑到了底部。

下面看下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}

class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
List<int> items = List.generate(16, (i) => i);
ScrollController _scrollController = new ScrollController();

@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print("loadMore");
_getMoreData();
}
});
}

Future _getMoreData() async {
List<int> newEntries =
await mokeHttpRequest(items.length, items.length + 10);
setState(() {
items.addAll(newEntries);
});
}

Future<List<int>> mokeHttpRequest(int from, int to) async {
return Future.delayed(Duration(seconds: 2), () {
return List.generate(to - from, (i) => i + from);
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("LoadMore"),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(title: Text("Index$index"));
},
controller: _scrollController,
));
}

@override
void dispose() {
super.dispose();
_scrollController.dispose();
}
}

运行效果:

是的,看着上面的效果我们已经实现了下拉加载更多,但是如果在正在请求的过程中多次下拉就会造成多次加载更多的情况,所以我们还得对这个做下处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}

class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
bool isLoading = false;
List<int> items = List.generate(16, (i) => i);
ScrollController _scrollController = new ScrollController();

@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print("loadMore");
_getMoreData();
}
});
}
Future _getMoreData() async {
if (!isLoading) {
setState(() => isLoading = true);
List<int> newEntries = await mokeHttpRequest(items.length, items.length + 10);
setState(() {
items.addAll(newEntries);
isLoading = false;
});
}
}
Future<List<int>> mokeHttpRequest(int from, int to) async {
return Future.delayed(Duration(seconds: 2), () {
return List.generate(to - from, (i) => i + from);
});
}


@override
Widget build(BuildContext context) {

return Scaffold(
appBar: AppBar(
title: Text("LoadMore"),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("Index$index"));
},
controller: _scrollController,
)
);
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
}
}

可以看到,我们仅仅在上面代码的基础上加上了一个isLoading的变量,当这个变量的值为true时,就不会触发加载更多的操作。


嗯,这样貌似就可以了,但是你在加载更多的过程总应该让用户看得到吧,比如给用户一个提示啊。

好吧,我们尝试在下拉的过程中给用个提示。

首先我们创建加载更多时显示的Vidget

1
2
3
4
5
6
7
8
Widget _buildLoadText() {
return Container(child: Padding(
padding: const EdgeInsets.all(18.0),
child: Center(
child: Text("加载中……"),
),
),color: Colors.white70,);
}

其实就是一个简单的Text哈。

然后修改ListView,使得itemCount数目加1,当是最后一条时显示加载中的View,不是最后一条显示正常的Widget

1
2
3
4
5
6
7
8
9
10
11
12

body: ListView.builder(
itemCount: items.length + 1,
itemBuilder: (context, index) {
if (index == items.length) {
return _buildLoadText();
} else {
return ListTile(title: Text("Index$index"));
}
},
controller: _scrollController,
)

好吧,我们还是来看下效果:

这样我们就实现了加载更多的提示效果,当然我们也可以试试其他的效果比如

实现起来也很简单,只是替换下加载更多的Item而已

1
2
3
4
5
6
7
8
9
10
11
Widget _buildProgressIndicator() {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: isLoadingMore ? 1.0 : 0.0,
child: new CircularProgressIndicator(),
),
),
);
}

当然,其他的效果大家可以根据自己的需要去自己实现哦

当然,上面的下拉刷新和加载更对可以同时用在一个ListView上面,这里就不再贴代码了,大家自己在下面试下。

小结

  • RefreshIndicator可以显示下拉刷新

  • 使用ScrollController可以监听滑动事件,判断当前view所处的位置

  • 可以根据item所处的位置来处理加载更多显示效果

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

打赏请备注姓名或者昵称,方便我后期统计哦

关注公众号,及时查阅最新文章