Flutter 下拉刷新、上拉加载有很多第三方插件,本文使用插件为:pull_to_refresh
目前pull_to_refresh在pub.dev上的使用情况:
刷新header的类型:
ClassicHeader
const ClassicHeader({Key? key,RefreshStyle refreshStyle: RefreshStyle.Follow,double height: 60.0,Duration completeDuration: const Duration(milliseconds: 600),this.outerBuilder,this.textStyle: const TextStyle(color: Colors.grey),this.releaseText,//松手加载this.refreshingText,//正在加载this.canTwoLevelIcon,this.twoLevelView,this.canTwoLevelText,this.completeText,//加载完成this.failedText,this.idleText,//下拉加载this.iconPos: IconPosition.left,this.spacing: 15.0,this.refreshingIcon,this.failedIcon: const Icon(Icons.error, color: Colors.grey),this.completeIcon: const Icon(Icons.done, color: Colors.grey),this.idleIcon = const Icon(Icons.arrow_downward, color: Colors.grey),this.releaseIcon = const Icon(Icons.refresh, color: Colors.grey),})
WaterDropHeader
const WaterDropHeader({Key? key,this.refresh,this.complete,Duration completeDuration: const Duration(milliseconds: 600),this.failed,this.waterDropColor: Colors.grey,this.idleIcon: const Icon(Icons.autorenew,size: 15,color: Colors.white,),
MaterialClassicHeader
const MaterialClassicHeader({Key? key,double height: 80.0,this.semanticsLabel,this.semanticsValue,this.color,double offset: 0,this.distance: 50.0,this.backgroundColor,})
WaterDropMaterialHeader
const WaterDropMaterialHeader({Key? key,String? semanticsLabel,double distance: 60.0,double offset: 0,String? semanticsValue,Color color: Colors.white,Color? backgroundColor,})
BezierHeader
BezierHeader({this.child: const Text(""),this.onOffsetChange,this.onModeChange,this.readyRefresh,this.enableChildOverflow: false,this.endRefresh,this.onResetValue,this.dismissType: BezierDismissType.RectSpread,this.rectHeight: 70,this.bezierColor})
TwoLevelHeader
const TwoLevelHeader({Key? key,this.height: 80.0,this.decoration,this.displayAlignment: TwoLevelDisplayAlignment.fromBottom,this.completeDuration: const Duration(milliseconds: 600),this.textStyle: const TextStyle(color: const Color(0xff555555)),this.releaseText,this.refreshingText,this.canTwoLevelIcon,this.canTwoLevelText,this.completeText,this.failedText,this.idleText,this.iconPos: IconPosition.left,this.spacing: 15.0,this.refreshingIcon,this.failedIcon: const Icon(Icons.error, color: Colors.grey),this.completeIcon: const Icon(Icons.done, color: Colors.grey),this.idleIcon = const Icon(Icons.arrow_downward, color: Colors.grey),this.releaseIcon = const Icon(Icons.refresh, color: Colors.grey),this.twoLevelWidget});
CustomHeader
const CustomHeader({Key? key,required this.builder, //HeaderBuilder(BuildContext context, RefreshStatus? mode);第二个参数刷新的状态,根据状态显示对应的刷新内容this.readyToRefresh, //准备刷新的回调this.endRefresh,//结束刷新的回调this.onOffsetChange,//下拉距离改变的回调this.onModeChange,//下拉状态改变的回调--RefreshStatusthis.onResetValue,double height: 60.0,Duration completeDuration: const Duration(milliseconds: 600),RefreshStyle refreshStyle: RefreshStyle.Follow, //设置下拉的样式})
刷新fotter的类型:
ClassicFooter
const ClassicFooter({Key? key,VoidCallback? onClick,LoadStyle loadStyle: LoadStyle.ShowAlways,double height: 60.0,this.outerBuilder,this.textStyle: const TextStyle(color: Colors.grey),this.loadingText,this.noDataText,this.noMoreIcon,this.idleText,this.failedText,this.canLoadingText,this.failedIcon: const Icon(Icons.error, color: Colors.grey),this.iconPos: IconPosition.left,this.spacing: 15.0,this.completeDuration: const Duration(milliseconds: 300),this.loadingIcon,this.canLoadingIcon: const Icon(Icons.autorenew, color: Colors.grey),this.idleIcon = const Icon(Icons.arrow_upward, color: Colors.grey),})
CustomFooter
const CustomFooter({Key? key,double height: 60.0,this.onModeChange,this.onOffsetChange,this.readyLoading,this.endLoading,LoadStyle loadStyle: LoadStyle.ShowAlways,//下拉加载样式required this.builder,Function? onClick,})
页面刷新、加载通用模块可以进行封装,封装header继承ClassicHeader,封装fotter继承ClassicFooter
class RefreshHeader extends ClassicHeader {}class LoadingFotter extends ClassicFooter {}
代码示例
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';class RefreshPage extends StatefulWidget {RefreshPage({Key? key}) : super(key: key);@override_RefreshPageState createState() => _RefreshPageState();
}class _RefreshPageState extends State<RefreshPage> {// 定义原始数据List<String> items = ["1", "2", "3", "4", "5", "6", "7", "8"];// 定义刷新控制器RefreshController _refreshController =RefreshController(initialRefresh: false);void _onRefresh() async {// 模拟网络请求,此处延时1秒await Future.delayed(Duration(milliseconds: 1000));// 刷新成功,数据恢复原样items = ["1", "2", "3", "4", "5", "6", "7", "8"];if (mounted) {setState(() {});}// 重置获取数据LoadStatus_refreshController.refreshCompleted(resetFooterState: true);}void _onLoading() async {// 模拟网络请求,此处延时1秒await Future.delayed(Duration(milliseconds: 1000));// 每次模拟加载数据,等到数据加载到20个为止,模拟数据都获取完成, 并设置LoadStatusif (items.length >= 20) {_refreshController.loadNoData();return;}//每次加载两个数据items.add((items.length + 1).toString());items.add((items.length + 1).toString());if (mounted) {setState(() {});}_refreshController.loadComplete();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Scaffold'),),body: SmartRefresher(enablePullDown: true, // 下拉刷新enablePullUp: true, // 上拉加载数据// header: ClassicHeader(// refreshStyle: RefreshStyle.UnFollow,// refreshingText:'正在加载...',// releaseText: '松手加载...',// completeText: '加载完成',// idleText: '下拉加载',// ),// header: ClassicHeader(),// header: WaterDropHeader(),// header: MaterialClassicHeader(),// header: WaterDropMaterialHeader(),// header: BezierHeader(),// header: TwoLevelHeader(),header: CustomHeader(refreshStyle: RefreshStyle.Behind,builder: (BuildContext context, RefreshStatus? mode) {Widget headerBody;if(mode == RefreshStatus.idle) {headerBody = Text('刷新');}else if(mode == RefreshStatus.refreshing) {headerBody = Text('刷新中...');}else if(mode == RefreshStatus.failed) {headerBody = Text('刷新失败');}else if(mode == RefreshStatus.completed) {headerBody = Text('刷新完成');}else if(mode == RefreshStatus.canRefresh) {headerBody = Text('松手刷新');}else {headerBody = Text("完成");}return Container(height: 55.0,child: Center(child: headerBody),);},),// footer: ClassicFooter(),footer: CustomFooter(// 设置上拉、下拉时的提示内容builder: (context, mode) {Widget body;if (mode == LoadStatus.idle) {body = Text("上拉加载");} else if (mode == LoadStatus.loading) {// body = CupertinoActivityIndicator();body = Text("加载中");} else if (mode == LoadStatus.failed) {body = Text("加载失败");} else if (mode == LoadStatus.canLoading) {body = Text("松手加载");} else {body = Text("没有更多数据");}return Container(height: 55.0,child: Center(child: body),);},),controller: _refreshController,onRefresh: _onRefresh,onLoading: _onLoading,child: ListView.builder(itemBuilder: (c, i) => Card(child: Center(child: Text(items[i]))),itemExtent: 100.0,itemCount: items.length,),),);}
}
特殊情况:
当当前数据不满一个屏幕时,此时展示上拉加载会先加载,然后无数据提示紧跟列表后面,如果示:
当数据不足一个屏幕时,不进行上拉加载数据的操作
结果图:
代码示例:
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';class MePage extends StatefulWidget {MePage({Key? key}) : super(key: key);@override_RefreshPageState createState() => _RefreshPageState();
}class _RefreshPageState extends State<MePage> {// 定义原始数据List<String> items = ["1","2",];bool isLoad = true;// 定义刷新控制器RefreshController _refreshController =RefreshController(initialRefresh: false);@overridevoid initState() {super.initState();}void _onRefresh() async {// 模拟网络请求,此处延时1秒await Future.delayed(Duration(milliseconds: 1000));// 刷新成功,数据恢复原样items = ["1","2","3","4","3","4","3","4",];if (mounted) {setState(() {});}// 重置获取数据LoadStatus_refreshController.refreshCompleted(resetFooterState: true);}void _onLoading() async {// 模拟网络请求,此处延时1秒await Future.delayed(Duration(milliseconds: 1000));// 每次模拟加载数据,等到数据加载到20个为止,模拟数据都获取完成, 并设置LoadStatusif (items.length >= 2) {_refreshController.loadNoData();return;}//每次加载两个数据items.add((items.length + 1).toString());if (mounted) {setState(() {});}_refreshController.loadComplete();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Scaffold'),),body: RefreshConfiguration(footerTriggerDistance: 80,// dragSpeedRatio: 0.91,hideFooterWhenNotFull: true,headerBuilder: () => ClassicHeader(),footerBuilder: () => ClassicFooter(),child: SmartRefresher(dragStartBehavior: DragStartBehavior.down,enablePullDown: true, // 下拉刷新enablePullUp: true, // 上拉加载数据controller: _refreshController,onRefresh: _onRefresh,onLoading: _onLoading,child: ListView.builder(itemBuilder: (c, i) => Card(child: Center(child: Text(items[i],style: TextStyle(fontSize: 10.0),))),itemExtent: 100.0,itemCount: items.length,),),),);}
}
fotter、header的位置,可以根据需求添加位置和定制样式
RefreshConfiguration说明:
RefreshConfiguration({Key? key,required this.child, //Widgetthis.headerBuilder, //下拉刷新header显示内容this.footerBuilder, //上拉加载footer显示内容this.dragSpeedRatio: 1.0,//拖拽速度this.shouldFooterFollowWhenNotFull, //当数据不足一个屏幕,遵循的状态this.enableScrollWhenTwoLevel: true, 两屏时用户是否可以拖动视口this.enableLoadingWhenNoData: false,//当footer处于nomore状态时,是否通过达到footerDistance来触发负载this.enableBallisticRefresh: false,//是否通过BallisticScrollActivity触发刷新this.springDescription: const SpringDescription(mass: 2.2,stiffness: 150,damping: 16,), //自定义弹簧动画this.enableScrollWhenRefreshCompleted: false,//当刷新完成并返回时,用户是否可以拖动viewportthis.enableLoadingWhenFailed: true, //失败时,footer是否可以通过达到footerDistance来触发负载this.twiceTriggerDistance: 150.0,//触发器twoLevel的超滚动距离this.closeTwoLevelDistance: 80.0, //关闭二层的底部穿越距离,前提:enableScrollWhenTwoLevel为truethis.skipCanRefresh: false, //如果到达triggerDistance时需要立即刷新this.maxOverScrollExtent, //超出边缘时的最大顶部滚动距离 --headerthis.enableBallisticLoad: true,//是否通过BallisticScrollActivity触发加载this.maxUnderScrollExtent, //超出边缘时最大底部滚动距离-- fotterthis.headerTriggerDistance: 80.0,//触发刷新的超滚距离this.footerTriggerDistance: 15.0, //触发加载后的距离this.hideFooterWhenNotFull: false, //当listView数据很小(不够一页)时,它应该被隐藏this.enableRefreshVibrate: false, // 刷新 开关this.enableLoadMoreVibrate: false,//加载 loadmore开关this.topHitBoundary,//边界位于上边缘,当惯性滚动超过边界距离时停止this.bottomHitBoundary,//边界位于底部边缘,当惯性在边界距离以下滚动时停止})