持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情
- 本文主要介绍三方组件中刷新组件,下拉刷新,上拉加载更多。
1. 刷新组件
对于刷新组件一些经过时间的考验,基本上都解决了问题,类似我们MJRefresh
,使用的人比较多
比如:pull_to_refresh: ^2.0.0
或者 flutter_easyrefresh: ^2.2.1
基本上都是可以解决我们的需求,自定义上拉下拉动画,文字描述等。这里我使用的是pull_to_refresh
,这里作者适配了flutter 3.0 适配 我们依赖的时候添加git地址
pull_to_refresh:
git:
url: https://github.com/miquelbeltran/flutter_pulltorefresh
2. pull_to_refresh
这里介绍下pull_to_refresh
的使用。组件的特性如下:
- 提供上拉加载和下拉刷新
- 几乎适合所有部件
- 提供全局设置默认指示器和属性
- 提供多种比较常用的指示器
- 支持Android和iOS默认滑动引擎,可限制越界距离,打造自定义弹性动画,速度,阻尼等。
- 支持水平和垂直刷新,同时支持翻转列表(四个方向)
- 提供多种刷新指示器风格:跟随,不跟随,位于背部,位于前部, 提供多种加载更多风格
- 提供二楼刷新,可实现类似淘宝二楼,微信二楼,携程二楼
- 允许关联指示器存放在Viewport外部,即朋友圈刷新效果
这里我们看下简单的使用,这里使用默认的上拉和下拉Widget
class RefreshPage extends StatefulWidget {
const RefreshPage({Key? key}) : super(key: key);
@override
State<RefreshPage> createState() => _RefreshPageState();
}
class _RefreshPageState extends State<RefreshPage> {
List<String> items = ["1", "2", "3", "4", "5", "6", "7", "8"];
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
void _onRefresh() async{
// monitor network fetch
await Future.delayed(const Duration(milliseconds: 1000));
// if failed,use refreshFailed()
items = ["1", "2", "3", "4", "5", "6", "7", "8"];
_refreshController.refreshCompleted();
if(mounted) {
setState(() {
});
}
}
void _onLoading() async{
// monitor network fetch
await Future.delayed(const Duration(milliseconds: 1000));
// if failed,use loadFailed(),if no data return,use LoadNodata()
items.add((items.length+1).toString());
if(mounted) {
setState(() {
});
}
_refreshController.loadComplete();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(Get.arguments['title']),),
body: SmartRefresher(
enablePullUp: true,
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
child: ListView.builder(
physics: const ClampingScrollPhysics(),
itemBuilder: (c, i) => Card(child: Center(child: Text(items[i],style: const TextStyle(color: Colors.black),))),
itemExtent: 100.0,
itemCount: items.length,
),
),
);
}
}
下拉刷新
上拉加载的时候enablePullUp
设置为true,我们模拟往数据中加载2条
我们也可以自定义一些
对于下拉刷新头我们可以使用默认WaterDropHeader
上拉刷新的footer我们可以像案列中一样自定义
CustomFooter get buildCustomFooter {
return CustomFooter(
builder: (BuildContext context, LoadStatus? mode) {
Widget body;
if (mode == LoadStatus.idle) {
body = const Text("pull up load");
} else if (mode == LoadStatus.loading) {
body = const CupertinoActivityIndicator();
} else if (mode == LoadStatus.failed) {
body = const Text("Load Failed!Click retry!");
} else if (mode == LoadStatus.canLoading) {
body = const Text("Release to Load more");
} else {
body = const Text("No more Data");
}
return Container(
height: 55.0,
child: Center(child: body),
);
},
);
}
这样一个简单的下拉刷新和上拉加载就完成了
3. 全局配置RefreshConfiguration
我们实际开发肯定要对其进行二次封装,首先我们知道官方说全局配置子树下的SmartRefresher
// 全局配置子树下的SmartRefresher,下面列举几个特别重要的属性
RefreshConfiguration(
headerBuilder: () => WaterDropHeader(), // 配置默认头部指示器,假如你每个页面的头部指示器都一样的话,你需要设置这个
footerBuilder: () => ClassicFooter(), // 配置默认底部指示器
headerTriggerDistance: 80.0, // 头部触发刷新的越界距离
springDescription:SpringDescription(stiffness: 170, damping: 16, mass: 1.9), // 自定义回弹动画,三个属性值意义请查询flutter api
maxOverScrollExtent :100, //头部最大可以拖动的范围,如果发生冲出视图范围区域,请设置这个属性
maxUnderScrollExtent:0, // 底部最大可以拖动的范围
enableScrollWhenRefreshCompleted: true, //这个属性不兼容PageView和TabBarView,如果你特别需要TabBarView左右滑动,你需要把它设置为true
enableLoadingWhenFailed : true, //在加载失败的状态下,用户仍然可以通过手势上拉来触发加载更多
hideFooterWhenNotFull: false, // Viewport不满一屏时,禁用上拉加载更多功能
enableBallisticLoad: true, // 可以通过惯性滑动触发加载更多
child: MaterialApp(
........
)
);
我们创建一个Widget
Widget refreshScaffold({required Widget child}) => RefreshConfiguration(
headerBuilder: () => const WaterDropHeader(), footerBuilder: () => const ClassicFooter(), child: child);
在入口包裹
这样我们对一些页面就不用在每个页面配置header和footer等组件了,统一在RefreshConfiguration
设置即可。
4. 自定义封装数据请求逻辑
我们一般分页的时候也是对数据进行判断处理,我们可以抽出来这个类进行处理
enum RefreshType { refresh, loadMore }
class PagingData<T> {
int count;
List<T> items;
int current = 1;
PagingData(this.count, this.items);
factory PagingData.fromJson(Map<String, dynamic> json, T Function(Map<String, dynamic>) decoder) {
final count = json['count'];
final items = (json['results'] as List<dynamic>).map((e) => decoder(e)).toList();
return PagingData(count, items);
}
@override
String toString() => '分页: $count';
bool get isEnd => items.length >= count;
void merge(PagingData<T> other, RefreshType refreshType) {
if (refreshType == RefreshType.refresh) items.clear();
count = other.count;
items.addAll(other.items);
}
void prepare(RefreshType refreshType) => current = refreshType == RefreshType.refresh ? 1 : (current + 1);
}
根据RefreshType
类型,进行对应的处理
mixin RefreshMixin<T> {
final refreshController = RefreshController(initialRefresh: true);
List<T> get items => paging.items;
final PagingData<T> paging = PagingData(0, []);
Future<PagingData> startRefresh(RefreshType refreshType) {
paging.prepare(refreshType);
return refreshRequest.then((value) {
paging.merge(value, refreshType);
if (refreshType == RefreshType.refresh) {
refreshController.refreshCompleted(resetFooterState: true);
if (paging.isEnd) refreshController.loadNoData();
} else {
paging.isEnd ? refreshController.loadNoData() : refreshController.loadComplete();
}
return paging;
});
}
Future<PagingData<T>> get refreshRequest => throw UnimplementedError('子类必须重写');
}
我们处理刷新请求,根据请求是否是刷新还是加载更多进行判断,同时判断是否是加载完成,改变状态。 对于请求子类必须重写。 比如我们的帖子类实现该协议,进行上拉下拉
class PostListController extends GetxController with RefreshMixin<Post>, NetMixin {
/// 刷新
onRefresh() {
startRefresh(RefreshType.refresh).then((value) => update());
}
/// 加载更多
onLoading() {
startRefresh(RefreshType.loadMore).then((value) => update());
}
@override
Future<PagingData<Post>> get refreshRequest => get('post/', (data) => PagingData.fromJson(data, Post.fromJson),
query: {'page': paging.current.toString(), 'start_id': items.isNotEmpty ? items.first.id.toString() : ''});
}
重写我们的refreshRequest
这样一个请求就处理好了
4.小结
我们在使用一些优秀的三方组件的时候可以多了解了解,有时间的话。看下其实现的思想,对于刷新的逻辑基本就是那么一套,我们可以根据自己开发经验,抽出来做成公共类
。
今天的文章Flutter学习之刷新组件-pull_to_refresh分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22099.html