下拉刷新的默认控件RefreshIndicator,其实就是android里面的SwipeRefreshLayout一个效果;
这里有个和android不一样的概念就是future,官方给的解释:
A Future is used to represent a potential value, or error, that will be available at some time in the future. Receivers of a Future can register callbacks that handle the value or error once it is available.
理解:
Future就是event,很多Flutter内置的组件比如Http(http请求控件)的get函数、RefreshIndicator(下拉手势刷新控件)的onRefresh函数都是event。每一个被await标记的句柄也是一个event,每创建一个Future就会把这个Future扔进event queue中排队等候安检~
另外还有一个Completer,其实就是触发下拉刷新完成时调用的控制器;
下拉刷新几个要注意的点(以ListView为例)
flutter默认是没有上拉加载的,只能自己实现,实现的原理的话,其实就是给listview增加一个item,控制其为加载更多的item或者是已经没有更多数据的item;
这里用了一个开源库:flutter_spinkit,效果还不错;
添加依赖:
dependencies:
flutter_spinkit: "^3.0.0"
效果如下:
代码如下:
class PersonalPage extends StatefulWidget {
PersonalPage({Key key}) : super(key: key);
@override
_PersonalPageState createState() => new _PersonalPageState();
}
class _PersonalPageState extends State {
List widgets = [];
final ScrollController _scrollController = new ScrollController();
//是否在加载
bool isLoading = false;
//是否有更多数据
bool isHasNoMore = false;
//当前页
var currentPage = 0;
//一页的数据条数
final int pageSize = 20;
//这个key用来在不是手动下拉,而是点击某个button或其它操作时,代码直接触发下拉刷新
final GlobalKey _refreshIndicatorKey = new GlobalKey<
RefreshIndicatorState>();
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_getMoreData();
}
});
loadData(false);
}
Widget getItem(int index) {
print("${index == widgets.length}");
if (index == widgets.length) {
if (isHasNoMore) {
return _buildNoMoreData();
} else {
return _buildLoadMoreLoading();
}
} else {
return itemBuilder(context, index);
}
}
@override
Widget build(BuildContext context) {
return new RefreshIndicator(
///GlobalKey,用户外部获取RefreshIndicator的State,做显示刷新
key: _refreshIndicatorKey,
onRefresh: refreshHelper,
child: new ListView.builder(
///保持ListView任何情况都能滚动,解决在RefreshIndicator的兼容问题。
physics: const AlwaysScrollableScrollPhysics(),
itemCount: widgets.length + 1,
controller: _scrollController,
itemBuilder: (context, index) {
return getItem(index);
},
),
);
}
Future refreshHelper() {
final Completer completer = new Completer();
//清空数据
widgets.clear();
currentPage = 0;
loadData(false, completer);
return completer.future;
}
//completer可选参数
void loadData(bool isLoadMore, [Completer completer]) async {
String url = "http://www.wanandroid.com/article/list/${currentPage}/json?cid=60";
if (isLoadMore) {
setState(() => isLoading = true);
}
var response = await HttpUtil().get(url,
startCallBack: () {
print("开始");
},
successCallBack: (var success) {
print("成功");
},
errorCallBack: () {
print("失败");
});
completer?.complete();
List data = response["data"]["datas"];
if (data.length < pageSize) {
isHasNoMore = true;
} else {
isHasNoMore = false;
}
if (isLoadMore) {
setState(() {
isLoading = false;
widgets.addAll(data);
});
} else {
setState(() {
widgets = data;
});
}
}
Widget itemBuilder(BuildContext context, int index) {
return new Container(
margin: EdgeInsets.all(15.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Text("标题: ${widgets[index]["title"]}", textAlign: TextAlign.start,
),
new Text(
"内容: ${widgets[index]["link"]}", textAlign: TextAlign.start,),
new Container(
margin: EdgeInsets.only(top: 10.0),
child: new Divider(
height: 2.0,
),
)
],
),
);
}
void _getMoreData() async {
if (!isLoading) {
currentPage++;
loadData(true);
}
}
Widget _buildLoadMoreLoading() {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: isLoading ? 1.0 : 0.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SpinKitFadingCircle(
color: Colors.grey,
size: 30.0,
),
new Padding(padding: EdgeInsets.only(left: 10)),
new Text("正在加载更多...")
],
),
),
),);
}
Widget _buildNoMoreData() {
return new Container(
margin: EdgeInsets.only(top: 15.0, bottom: 15.0),
alignment: Alignment.center,
child: new Text("没有更多数据了"),
);
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
}
}