上篇文章的原理是讲完了,但一样留下来不少让一个Android开发难堪的问题:
1、下拉刷新卡顿(这个可能会另外开篇讲这个问题,当前先保证业务逻辑是对的)
2、上拉加载一下子会更新好多次数据
3、数据真的拉完的时候接口还在请求
4、数据真的拉完的时候下方那条UI还在转圈
5、空页面时候点击一下就会触发第二页的刷新动作???
问题出来了,这就开始一点点说吧:
1、 先端出状态来!
对于以上问题2,那就该祭出来bool isLoading = false了!
我总得需要让前端UI知道后端正在加载吧,然后给请求方法小小的改造一下:
main_provider.dart节选
Future<void> getListPage(int pageNum) async {
isLoading = true;
await DioUtil.getInstance().get(
"http://192.168.5.116:20080/contact/listPage",
{
'page': pageNum,
'size': 10,
},
() {
_text = "loading";
notifyListeners();
},
(data) {
var bean = BaseBean.fromJson(data);
var pageBean = PageBean.fromJson(bean.data);
if(pageBean.current==1) {
_dataList.clear();
}
_dataList.addAll(List.from(pageBean.records!.map((e) => ContactBean.fromJson(e))));
isLoading = false;
notifyListeners();
},
(reason) {
_text = reason;
isLoading = false;
notifyListeners();
},
);
}
大概意思就是说调用该方法时候先把isLoading=true,任何情况下方法执行完成后(包括出错)isLoading=false,这样管他UI刷新洪水滔天,我说在加载就是在加载!
那UI怎么改呢?
main.dart节选
Expanded(
child: RefreshIndicator(
onRefresh: () async {
// 刷新时重置页码并重新加载数据
provider.resetPage();
await provider.getListPage(provider.pageNum);
},
child: NotificationListener<ScrollNotification>(
onNotification: (scrollInfo){
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
// 滚动到底部,加载下一页
if(!provider.isLoading){
provider.addPage();
provider.getListPage(provider.pageNum);
}
}
return false;
},
child: ListView.builder(
itemCount:provider.dataList.isEmpty? 0 : provider.dataList.length + 1,
itemBuilder: (context, index) {
if (index < provider.dataList.length) {
return ListTile(
title: Text('${provider.dataList[index].lastName} ${provider.dataList[index].firstName}'),
subtitle: Text('${provider.dataList[index].phoneNumber}'),
onTap: () async {
if(index%2==0){
showToast('点击了第${index+1}个联系人:${provider.dataList[index].lastName} ${provider.dataList[index].firstName}',context: context);
} else {
var data =await showADialog(context, '提示',
'点击了第${index + 1}个联系人:${provider
.dataList[index].lastName} ${provider
.dataList[index].firstName}');
showToast(data.toString(), context: context);
}
},
);
} else {
// 显示加载提示
return Center(child: CircularProgressIndicator());
}
},
),
),
)
)
当ListView UI滚动到底部的时候如果provider的isLoading为true的话就会忽略调用下一页的方法。
治完问题2,还有问题3和4,那就再祭出一个状态:bool finalPage = false
这个玩意对UI能通知确实是加载完了,对方法能拦截之后的无效加载。
再改一下provider
main_provider.dart节选
Future<void> getListPage(int pageNum) async {
if(finalPage){
return;
}
isLoading = true;
await DioUtil.getInstance().get(
"http://192.168.5.116:20080/contact/listPage",
{
'page': pageNum,
'size': 10,
},
() {
_text = "loading";
notifyListeners();
},
(data) {
var bean = BaseBean.fromJson(data);
var pageBean = PageBean.fromJson(bean.data);
if(pageBean.current==1) {
_dataList.clear();
}
_dataList.addAll(List.from(pageBean.records!.map((e) => ContactBean.fromJson(e))));
isLoading = false;
if(pageBean.current==pageBean.pages){
finalPage = true;
}
notifyListeners();
},
(reason) {
_text = reason;
isLoading = false;
notifyListeners();
},
);
}
当拉到接口的最后一页的时候就把finalPage=true。如果下次还要触发调接口,先看看finalPage,如果finalPage==true了直接return不执行啦!这样就能完美拦截接口这边的调用。
当然UI也得改:
main.dart节选
Expanded(
child: RefreshIndicator(
onRefresh: () async {
// 刷新时重置页码并重新加载数据
provider.resetPage();
await provider.getListPage(provider.pageNum);
},
child: NotificationListener<ScrollNotification>(
onNotification: (scrollInfo){
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
// 滚动到底部,加载下一页
if(!provider.isLoading || !provider.finalPage){
provider.addPage();
provider.getListPage(provider.pageNum);
}
}
return false;
},
child: ListView.builder(
itemCount:provider.dataList.isEmpty? 0 : provider.dataList.length + 1,
itemBuilder: (context, index) {
if (index < provider.dataList.length) {
return ListTile(
title: Text('${provider.dataList[index].lastName} ${provider.dataList[index].firstName}'),
subtitle: Text('${provider.dataList[index].phoneNumber}'),
onTap: () async {
if(index%2==0){
showToast('点击了第${index+1}个联系人:${provider.dataList[index].lastName} ${provider.dataList[index].firstName}',context: context);
} else {
var data =await showADialog(context, '提示',
'点击了第${index + 1}个联系人:${provider
.dataList[index].lastName} ${provider
.dataList[index].firstName}');
showToast(data.toString(), context: context);
}
},
);
} else {
// 显示加载提示
return Center(child: provider.finalPage
? Text('没有更多数据了')
: CircularProgressIndicator()
);
}
},
),
),
)
)
在检查是否正在加载那再加一个条件,达到“如果没有加载或不是最后一页再去刷新”的逻辑效果
下面的加载提示也可以借这个条件可以变换一下,加载完最后一行就可以提示没有更多数据了。
注:记得在provider中刷新的时候将状态归位哈!
2、问题5的发现和解决方法
正当我以为大功告成的时候,我貌似碰了一下屏幕就触发刷新数据了——但很明显不是第一页的!
数据库查看、接口日志全部指向第二页!
虽说provider中_pageNum默认是1,但这回触发的不是下拉刷新是上拉加载,我第一页数据还没薅回来呢直接薅第二页?
那_pageNum默认给0吧,反正刷新后会给1,如果触发加载的话0+1=1嘛
收工!
至于问题1,可能需要单开一篇看这个事情