Flutter为我们提供了相当丰富的控件,但是有时候还是不能满足大家的需求,官方的控件只有下拉刷新,没有上拉加载,感觉很别扭;我从安卓转过来的,所以我做了一套和安卓原生的一样效果的控件
首先请允许我介绍下我控件:理论上适配所有可滑动View, Android IOS 双平台通用. 上下拉分别可控, 可单独使用上拉或下拉; 支持上下拉头完全自定义,现已支持是否加载成功的状态通知,支持通过方法调用触发下拉刷新,重点:很少BUG!很少BUG!很少BUG! 看下效果图吧
可能好多人只是想用这个控件,那我们先看看怎么去使用,后面我们会讲解代码
第一步 添加下面的代码到 pubspec.yaml 文件
dependencies:
pulltorefresh_flutter: "^0.1.9"
第二步 如果使用本项目默认图片,请下载 https://raw.githubusercontent.com/baoolong/PullToRefresh_Flutter/master/images/refresh.png 等图片到你的images文件夹下,并在Pubspec.yaml添加如下配置
assets:
- images/refresh.png
第三步 在你要使用的文件里 导包
import 'package:pulltorefresh_flutter/pulltorefresh_flutter.dart';
第四步 写你的业务代码(以下为示例代码)
这里我示例加载github首页,由于我自定义了下拉头的文字提示,所以就有 “快尼玛给老子松手!”这个变量了,哈哈哈;要注意的是,有时ListView的Item太少而不能铺满屏幕,ListView 不能Scroll,导致PullToRefresh也不可使用,同时为了兼容IOS,所以初始化ScrollPhysics必须是RefreshAlwaysScrollPhysics。所以要定义RefreshAlwaysScrollPhysics这个变量,别忘了定义这个变量哦!build方法里面使用了上下拉控件:PullAndPush,并对各个参数进行了定义,下面是代码
///PullAndPush可以使用默认的样式,在此样式的基础上可以使用default**系列的属性改变显示效果,也可以自定义RefreshBox的样式(footerRefreshBox or headerRefreshBox),也就是说可以定义其中一个,另一个用默认的,也可以全部自定义
///isPullEnable;isPushEnable属性可以控制RefreshBox 是否可用,无论是自定义的还是默认的
///PullAndPush can use the default style,Based on this style, you can use the properties of the default** series to change the display,
///You can also customize the style of the RefreshBox (footerRefreshBox or headerRefreshBox), which means you can define one of them, and the other can be customized by default or all.
class PullAndPushTestState extends State<PullAndPushTest> with TickerProviderStateMixin{
List<String> addStrs=["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
List<String> strs=["1","2","3","4","5","6","7","8","9","0"];
ScrollController controller=new ScrollController();
//For compatibility with ios ,must use RefreshAlwaysScrollPhysics ;为了兼容ios 必须使用RefreshAlwaysScrollPhysics
ScrollPhysics scrollPhysics=new RefreshAlwaysScrollPhysics();
//使用系统的请求
var httpClient = new HttpClient();
var url = "https://github.com/";
var _result="";
String customRefreshBoxIconPath="images/icon_arrow.png";
AnimationController customBoxWaitAnimation;
int rotationAngle=0;
String customHeaderTipText="快尼玛给老子松手!";
String defaultRefreshBoxTipText="快尼玛给老子松手!";
///button等其他方式,通过方法调用触发下拉刷新
TriggerPullController triggerPullController=new TriggerPullController();
@override
void initState() {
super.initState();
//这个是刷新时控件旋转的动画,用来使刷新的Icon动起来
customBoxWaitAnimation=new AnimationController(duration: const Duration(milliseconds: 1000*100), vsync: this);
//第一次layout后会被调用
WidgetsBinding.instance.addPostFrameCallback((context){
print("addPostFrameCallback is invoke");
//triggerPullController.triggerPull();
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("上下拉刷新"),
),
body: new PullAndPush(
//如果你headerRefreshBox和footerRefreshBox全都自定义了,则default**系列的属性均无效,假如有一个RefreshBox是用默认的(在该RefreshBox Enable的情况下)则default**系列的属性均有效
//If your headerRefreshBox and footerRefreshBox are all customizable,then the default** attributes of the series are invalid,
// If there is a RefreshBox is the default(In the case of the RefreshBox Enable)then the default** attributes of the series are valid
defaultRefreshBoxTipText: defaultRefreshBoxTipText,
headerRefreshBox: _getCustomHeaderBox(),
triggerPullController:triggerPullController,
//你也可以自定义底部的刷新栏;you can customize the bottom refresh box
animationStateChangedCallback:(AnimationStates animationStates,RefreshBoxDirectionStatus refreshBoxDirectionStatus){
_handleStateCallback( animationStates, refreshBoxDirectionStatus);
},
listView: new ListView.builder(
//ListView的Item
itemCount: strs.length,//+2,
controller: controller,
physics: scrollPhysics,
itemBuilder: (BuildContext context,int index){
return new Container(
height: 35.0,
child: new Center(
child: new Text(strs[index],style: new TextStyle(fontSize: 18.0),),
),
);
}
),
loadData: (isPullDown) async{
await _loadData(isPullDown);
},
scrollPhysicsChanged: (ScrollPhysics physics) {
//这个不用改,照抄即可;This does not need to change,only copy it
setState(() {
scrollPhysics=physics;
});
},
)
);
}
}
上面代码那么多变量,看起来是不是很懵逼?到底有啥用呢?下面我来一一展示变量的用途
triggerPullController | 可通过此对象的方法调用主动触发下拉刷新 |
loadData | 加载数据的回调 |
scrollPhysicsChanged | ListView的scrollPhysics发生改变时回调,通过SetState改变ListView的Physics, 这个回调直接抄demo里面的就行 |
listView | ListView控件 |
isShowLeadingGlow | 在拉到顶部时,是否显示光晕效果,默认不显示 |
isShowTrailingGlow | 在拉到底部时,是否显示光晕效果,默认不显示 |
backgroundColor | 设置上下拉框的颜色 |
refreshIconPath | 设置上下拉框中旋转的图片的路径 |
tipText | 设置上下拉框中的提示文字 |
textColor | 设置上下拉框中文字的颜色 |
isPullEnable | 是否使用下拉刷新,默认启用 |
isPushEnable | 是否启用上拉加载,默认启用; |
glowColor | 设置光晕的颜色,默认为蓝色; |
headerRefreshBox ; footerRefreshBox | 分别 自定义下拉头Box 和 上拉头部Box(就是说你想用自己设计的箭头,自己的字体,自己的等待动画,自己的图片等,那你就设计一个头部替换我写的默认头部) |
animationStateChangedCallback | 动画各种状态下的回调 |
你想怎么搞里面 是不是都有啊!很随意的。
下面是我自定义的头部,感觉还行,可以参考下↓↓↓
Widget _getCustomHeaderBox(){
return new Container(
color: Colors.grey,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Align(
alignment: Alignment.centerLeft,
child: new RotatedBox(
quarterTurns: rotationAngle,
child: new RotationTransition( //布局中加载时动画的weight
child: new Image.asset(
customRefreshBoxIconPath,
height: 45.0,
width: 45.0,
fit:BoxFit.cover,
),
turns: new Tween(
begin: 100.0,
end: 0.0
)
.animate(customBoxWaitAnimation)
..addStatusListener((animationStatus) {
if (animationStatus == AnimationStatus.completed) {
customBoxWaitAnimation.repeat();
}
}
),
),
),
),
new Align(
alignment: Alignment.centerRight,
child:new ClipRect(
child:new Text(customHeaderTipText,style: new TextStyle(fontSize: 18.0,color: Color(0xffe6e6e6)),),
),
),
],
)
);
}
↓↓↓有人吐槽Pub上的上下拉刷新的状态不好控制,不准确,我的状态可谓是丰富啊(这都是删了一些状态后的结果),先看看我的回调状态:animationStateChangedCallback的回调状态
DragAndRefreshEnabled | RefreshBox高度达到50 , 可触发上下拉刷新,可提示用户松手即可刷新 |
StartLoadData | 开始加载数据时,可显示加载等待的动画 |
LoadDataEnd | 加载完数据时;RefreshBox会留在屏幕2秒,并不马上消失,提示用户是否加载成功 |
RefreshBoxIdle | RefreshBox已经消失,并且闲置,不管是未触发刷新还是刷新结束,可通过该通知重值图片,提示等状态 |
有了上面的状态,简直可以为所欲为啊,是不是能满足大多数的需求,看下代码是怎么用的吧
void _handleStateCallback(AnimationStates animationStates,RefreshBoxDirectionStatus refreshBoxDirectionStatus){
switch (animationStates){
//RefreshBox高度达到50,上下拉刷新可用;RefreshBox height reached 50,the function of load data is available
case AnimationStates.DragAndRefreshEnabled:
setState(() {
//3.141592653589793是弧度,角度为180度,旋转180度;3.141592653589793 is radians,angle is 180⁰,Rotate 180⁰
rotationAngle=2;
});
break;
//开始加载数据时;When loading data starts
case AnimationStates.StartLoadData:
setState(() {
customRefreshBoxIconPath="images/refresh.png";
customHeaderTipText="正尼玛在拼命加载.....";
});
customBoxWaitAnimation.forward();
break;
//加载完数据时;RefreshBox会留在屏幕2秒,并不马上消失,这里可以提示用户加载成功或者失败
// After loading the data,RefreshBox will stay on the screen for 2 seconds, not disappearing immediately,Here you can prompt the user to load successfully or fail.
case AnimationStates.LoadDataEnd:
customBoxWaitAnimation.reset();
setState(() {
rotationAngle = 0;
if(refreshBoxDirectionStatus==RefreshBoxDirectionStatus.PULL) {
customRefreshBoxIconPath = "images/icon_cry.png";
customHeaderTipText = "加载失败!请重试";
}else if(refreshBoxDirectionStatus==RefreshBoxDirectionStatus.PUSH){
defaultRefreshBoxTipText="可提示用户加载成功Or失败";
}
});
break;
//RefreshBox已经消失,并且闲置;RefreshBox has disappeared and is idle
case AnimationStates.RefreshBoxIdle:
setState(() {
rotationAngle=0;
defaultRefreshBoxTipText=customHeaderTipText="快尼玛给老子松手!";
customRefreshBoxIconPath="images/icon_arrow.png";
});
break;
}
}
是不是思路清晰明确啊,也不是很难嘛
↓↓↓最后我补充下加载数据的方法,这个方法必须是一个Future方法,变量isPullDown用来判断是下拉还是上拉,true表示下拉刷新,False表示上拉加载
Future _loadData(bool isPullDown) async{
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
_result = await response.transform(utf8.decoder).join();
setState(() {
//拿到数据后,对数据进行梳理
if(isPullDown){
strs.clear();
strs.addAll(addStrs);
}else{
strs.addAll(addStrs);
}
});
} else {
_result = 'error code : ${response.statusCode}';
}
} catch (exception) {
_result = '网络异常';
}
print(_result);
}
到这里,怎么使用上下拉控件已经讲完了,虽然用起来有那么一点点复杂,可是自定义程度高,可扩展性强,稳定度高,需要写的代码思路也清晰,最最主要的:BUG少;如果在使用的过程中有什么问题,请留言,随时为你解答
最后附上源码地址:https://github.com/OpenFlutter/PullToRefresh;
里面有很多更酷的控件,欢迎Star;如果喜欢Flutter,可以加入我们哦,我们的QQ群是 :892398530