flutter怎么手动刷新_Flutter 之 “下拉刷新&上拉加载” 篇一

本篇主要讲解最基础的下拉刷新和上拉加载的用法,以后再做如何实现更加酷炫的效果。

现在Flutter Packages里面已经有人写好了的第三方库了,但是我们不应该局限于此,

我们要弄清方法,后面使用开源库才会知其然,知其所以然。

不仅仅只是会使用开源库提供的widget,然后:new SwipeRefreshLayout(xxxx),

这样的程序员 ‍ ‍大街上随便拉一个都可以做到。

冒着被媳妇嫌弃的风险,大晚上写文章,我偷偷告诉她写的是“婚后感言”

Flutter提供一个android样式的下拉刷新指示器,在android中下拉刷新控件是SwipeRefreshLayout,在Flutter中使用RefreshIndicator,用它来包裹一个可滚动的widget。RefreshIndicator

使用方法很简单,需要注意的是RefreshIndicator内部参数“ScrollPhysics”,

需要指定为"AlwaysScrollableScrollPhysics",确保滚动视图始终可以滚动。

总觉得不贴个代码很别扭,来个下拉刷新样例代码:

new RefreshIndicator(

child: new ListView.builder(

itemBuilder: (context, index) {

return new Container(

height: 20.0,

alignment: Alignment.topLeft,

key: new PageStorageKey(index),

child:new Text(mDataList[index].content),

);

},

physics: const AlwaysScrollableScrollPhysics(),

itemCount: mDataList.length,

),

//刷新方法 onRefresh: () => _handlerRefresh()

)

下面增加模拟网络加载耗时请求的方法:

Future _handlerRefresh() async {

//模拟耗时5秒 await new Future.delayed(new Duration(seconds: 5));

setState(() {

mDataList=generateData();

});

return null;

}

接下来进入上拉加载的探索,我们需要搞清楚下面两个问题:

问题一:使用什么来获取滚动偏移量?

1.我们可以使用ScrollController 滚动控制器:

添加监听器,用于监听当前偏移量等信息。

滚动控制器内部创建ScrollPosition以管理特定于单个Scrollable小部件的状态。

ScrollController 通常与ListView,GridView,CustomScrollView一起使用。

我们需要定义一个有状态的小部件RefreshState,在这个State中addListener来监听滚动信息,以及removeListener。State class Structure

我们需要重写这两个方法来add和remove我们的Listener

小插曲:

需要注意一点的是IOS和Android的列表在下拉上拉效果是不一样的,IOS自带弹性效果,那么我们可以通过控制ScrollPhysics来让IOS和Android效果一致:ClampingScrollPhysics :防止滚动偏移超出内容范围,Android列表的效果,到达边缘都是拉不动的。

BouncingScrollPhysics:iOS弹跳行为,超出内容边界,还可以继续拉出一段空白空间出来,松开回弹回去。

我们现在使用的是AlwaysScrollableScrollPhysics,使用它表示确保滚动视图始终可以滚动

如果你的列表没有超出屏幕,你不使用这个属性,你的列表是无法滑动的。如果你的列表确定是可以超出屏幕的,你自己选择把,我建议还是使用AlwaysScrollableScrollPhysics这个稳妥。

回到正题:

上面讲到ScrollController添加listener之后我们可以通过scrollController.position.pixels,可以获得滚动的view实际滚动偏移量。

而ScrollPosition是:确定滚动视图中可见的内容部分。

我们可以使用scrollPosition指定scrollView定位到对应的内容位置区域:

1.scrollController.position.jumpTo(double value);

2.scrollController.jumpTo(double value);这个实际上也是调用的是position.jumpTo(double value);

当然有同学发现"scrollController.offset"也是可以获得到滚动的view实际滚动偏移量,大哥,它指向的是scrollposition.pixels。

问题二:如何判断当前滚动到了列表底部?

有了问题一相关内容介绍,我都可以直接贴代码了,废话不多说,自己看源码注释解释

///maxScrollExtent 可以滑动的最大距离bool isBottom = widget.scrollController.position.pixels ==

widget.scrollController.position.maxScrollExtent;

这段代码是判断当前是否滚动到了列表底部。可能IOS设备比较恶心,上下弹的效果可能导致这个值比较的不是很准确,前提是你的第一页数据如果没有超出屏幕的情况下!

建议测试的时候,数据列表弄多一点,尽量超出屏幕,否则你会发现很多不如意的问题。

最后,写一个上拉加载更多,下拉刷新列表:

首先定义一个有状态小部件:

class PullToRefreshWidget extends StatefulWidget {

@override

State createState() => PullToRefreshState();

}

紧接着定义一个状态类,在状态类中定义一个ScrollController:

class PullToRefreshState extends State {

ScrollController _scrollController = new ScrollController();

@override

Widget build(BuildContext context) {

return new RefreshWidget(

scrollController: _scrollController,

);

}

}

为了做的比较让别人难懂,装逼下,在定义一个有状态小部件:

class RefreshWidget extends StatefulWidget {

RefreshWidget({this.scrollController});

final ScrollController scrollController;

@override

State createState() => RefreshState();

}

最后定义RefreshState:

class RefreshState extends State

在问题一中我们已经告诉大家如何addListener和removeListener:

@override

void initState() {

super.initState();

widget.scrollController.addListener(_updateScrollPosition);

mDataList = getDatas();

}

@override

void dispose() {

widget.scrollController.removeListener(_updateScrollPosition);

super.dispose();

}

加载更多如何显示呢?其实就是一个占位,我们使用SizedBox和Opacity组合使用:

new Container(

alignment: Alignment.center,

padding: EdgeInsets.all(5.0),

child: new SizedBox(//约束下 height: 40.0,

width: 40.0,

child: new Opacity(

opacity: isLoadingMore?1.0:0.0,//透明度,显示隐藏 child: new CircularProgressIndicator(),

),

),

)

上拉加载和下拉刷新,关键就是两个状态之间切换的时候,标志要判断对,就不会出现混乱,

目前还有一个问题是,无法禁用下拉加载,知道的老铁欢迎留言回复。

来个最终效果视频,最下方贴上RefreshState中的全部代码:https://www.zhihu.com/video/1005582083640553472

class RefreshState extends State {

bool isRefreshing = false;

bool isLoadingMore = false;

var mDataList;

final GlobalKey _refreshIndicatorKey =

new GlobalKey();

void bindItemData(List itemDatas) {

setState(() {

mDataList.addAll(itemDatas);

isLoadingMore = false;

isRefreshing = false;

});

}

void _updateScrollPosition() {

bool isBottom = widget.scrollController.position.pixels ==

widget.scrollController.position.maxScrollExtent;

/*print(">>>>isLoadingMore:$isLoadingMore""+isRefreshing:$isRefreshing""+isBottom:$isBottom");*/

if (!isLoadingMore && isBottom && !isRefreshing) {

setState(() {

isRefreshing = false;

isLoadingMore = true;

_loadMore();

});

}

}

Future _loadMore() async {

//模拟耗时3秒 await new Future.delayed(new Duration(seconds: 5));

bindItemData(generateData());

return null;

}

@override

void initState() {

super.initState();

widget.scrollController.addListener(_updateScrollPosition);

mDataList = getDatas();

}

@override

void dispose() {

widget.scrollController.removeListener(_updateScrollPosition);

super.dispose();

}

@override

Widget build(BuildContext context) {

return new RefreshIndicator(

child: _buildListView(), onRefresh: _handlerRefresh);

}

Widget _buildListView() {

return new ListView.builder(

key: _refreshIndicatorKey,

controller: widget.scrollController,

itemBuilder: (context, index) {

if (index == mDataList.length) {

return new Container(

alignment: Alignment.center,

padding: EdgeInsets.all(5.0),

child: new SizedBox(

height: 40.0,

width: 40.0,

child: new Opacity(

opacity: isLoadingMore?1.0:0.0,

child: new CircularProgressIndicator(),

),

),

);

}

return new Column(

children: [

new Container(

width: double.infinity,

height: 38.0,

alignment: Alignment.centerLeft,

key: new PageStorageKey(index),

child: new Text(mDataList[index].content,),

),

new Divider(

height: 2.0,

)

],

);

},

physics: const AlwaysScrollableScrollPhysics(),

itemCount: mDataList.length + 1,

);

}

Future _handlerRefresh() async {

if(!isLoadingMore){

setState(() {

isRefreshing = true;

isLoadingMore = false;

});

//模拟耗时3秒 await new Future.delayed(new Duration(seconds: 5));

setState(() {

mDataList = generateData();

isRefreshing = false;

isLoadingMore = false;

});

widget.scrollController.jumpTo(0.0);

}

//widget.scrollController.animateTo(0.0, duration: new Duration(milliseconds:100), curve: Curves.linear); return null;

}

}

晚安,各位老铁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值