持续创作,加速生长!这是我参加「日新方案 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.小结
咱们在运用一些优异的三方组件的时分能够多了解了解,有时间的话。看下其完成的思维,关于改写的逻辑根本就是那么一套,咱们能够依据自己开发经验,抽出来做成公共类
。