Open
Description
前言
目前,我们的app首页已经有个样子出来了,并且数据也不是开玩笑的,都是真实数据,对于首页而言,我们当然还需要添加翻页的功能,甚至包括下拉刷新的功能。幸运是的,Flutter对于这方面来说,给我们封装的还是相当的便利的。
修复上面请求接口bug
今天重新打开页面的时候发现了页面报错,debug了一下发现是接口数据过来有些字段为空导致的。遂这里请求接口以及数据的处理上,做了一些调整
- lib/util/data_utils.dart
try {
IndexCell cellData = new IndexCell.fromJson(responseList[i]);
resultList.add(cellData);
} catch (e) {
// No specified type, handles all
print('Something really unknown: $i');
}
getIndexListData
方法里面加了一层异常捕获,毕竟不是我们自己跟后端的约束,对业务也并非清楚,所以里面一些字段我们也不清楚,这些问题也是难免。
- lib/model/indexCell.dart
factory IndexCell.fromJson(Map<String, dynamic> json) {
String _tag = '';
if(json['tags'].length>0){
_tag = '${json['tags'][0]['title']}/';
}
return IndexCell(
hot: json['hot'],
collectionCount: json['collectionCount'],
commentCount: json['commentsCount'],
tag: '$_tag${json['category']['name']}',
username: json['user']['username'],
createdTime: Util.getTimeDuration(json['createdAt']),
title: json['title'],
detailUrl: json['originalUrl'],
isCollection: json['type'] ,
);
}
添加了对tag的为空判断
下拉刷新
下拉刷新其实就是在我们之前调用下我们的getList
方法。难点可能就是如何触发这个下拉刷新呢?非常幸运,Material 中 提供了 RefreshIndicator widget
同样,我们可以从他的源码中看到他的属性
- lib/index_page.dart
// 下拉刷新
Future<void> _onRefresh() async{//The RefreshIndicator onRefresh callback must return a Future.
_listData.clear();
setState(() {
_listData = _listData;
//注意这里需要重置一切请求条件
_hasMore = true;
});
getList(false);
return null;
}
//build 方法返回如下
return RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
itemCount: _listData.length + 2, //添加一个header 和 loadMore
itemBuilder: (context, index) => _renderList(context, index),
),
);
下拉刷新的方法中setState是为了重新build,毕竟空列表页面会有loading的出现。
loadMore的实现
- lib/index_page.dart
ListView.builder中有一个属性为Controller,他可以监听页面的滚动,所以我们在ListView.builder中加入这个属性
ScrollController _scrollController = new ScrollController();
// build方法中
return RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
itemCount: _listData.length + 2, //添加一个header 和 loadMore
itemBuilder: (context, index) => _renderList(context, index),
controller: _scrollController,
),
);
在页面初始化的时候键入滚动监听,并且触发getList方法
@override
void initState() {
super.initState();
getList(false);
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print('loadMore');
getList(true);
}
});
}
一些别的变量的作用,由于触底这个动作可以反复触发,而网络请求是异步的,所以我们不可能在每一次触底都要去发送过一次请求,而是再一次请求结束后,再次触底才会再次发送请求。所以这里我们加入了 _isRequesting 的flag
bool _isRequesting = false; //是否正在请求数据的flag
bool _hasMore = true;
请求的方法也做了稍微的跳转
getList(bool isLoadMore) {
if (_isRequesting || !_hasMore) return;
if (!isLoadMore) {
// reload的时候重置page
_pageIndex = 0;
}
_params['before'] = pageIndexArray[_pageIndex];
_isRequesting = true;
DataUtils.getIndexListData(_params).then((result) {
_pageIndex += 1;
List<IndexCell> resultList = new List();
if(isLoadMore){
resultList.addAll(_listData);
}
resultList.addAll(result);
setState(() {
_listData = resultList;
_hasMore = _pageIndex < pageIndexArray.length;
_isRequesting = false;
});
});
}
添加加载器
页面触底,发送请求,讲道理应该是要给用户一个反馈的,由于这个可以很多页面公用,所以当然,我们将其封装为一个widget
- lib/widgets/load_more.dart
import 'package:flutter/material.dart';
class LoadMore extends StatelessWidget {
final bool hasMore;
LoadMore(this.hasMore);
@override
Widget build(BuildContext context) {
if (hasMore) {
return Container(
height: 70.0,
child: Center(
child: Opacity(
opacity: 1.0,
child: CircularProgressIndicator(
strokeWidth: 3.0,
),
),
),
);
}
return Container(
height: 70.0,
child: Center(
child: Text('亲,我也是有底线的',
style: TextStyle(color: Theme.of(context).accentColor)),
),
);
}
}
最终,我们的页面效果就出来了。 代码地址
总结
通过这一章节,你应该学会
- Dart中异常的处理和捕获
- 下拉刷新
- 加载更多