基于EasyRefresh自定义RefreshListView

来到新公司之后,发现并没有现成的、好用的用来做分页列表数据加载的ListView,原先项目中虽然也基于EasyRefresh,但是很奇怪的是直接在绘制倒数第二个item的时候就触发onLoad方法,而且使用的时候还要继承某特定的类,既不合理又耦合严重,所以决定封装个CustomRefreshListView,方便以后在项目中使用。

一 构建

基于EasyRefresh,使用EasyRefresh.builder构造方法,在这里,headerfooter可以根据项目设计自定义,不再多言。下拉刷新和上拉加载更多都回调到外部,由外部处理。若总的item数等于itemCount,则表明所有的数据加载完毕。代码如下:

@override
Widget build(BuildContext context) {

  return EasyRefresh.builder(
      header:  widget.refreshHeader ?? MaterialHeader(),
      footer: widget.refreshFooter ?? BuilderFooter(
          triggerOffset: 0,
          clamping: false,
          builder: (BuildContext context, IndicatorState state) {
            return SizedBox();
          }),
      onRefresh: () async {
          widget.onRefresh();
        },
        onLoad: () async{
        if (widget.totalCount == null) {
          ///说明不需要分页加载
          print("无需分页加载");
          return;
        }
        if (widget.totalCount != null && widget.totalCount == widget.itemCount) {
          ///说明全部item加载完毕
          print("全部item加载完毕");
          return;
        }
        if (_isLoading.value == false && widget.onLoad != null) {
          widget.onLoad!();
          _isLoading.value = true;
          Future.delayed(widget.duration, () {
            _isLoading.value = false; // 重置
          });
        }
      },
      childBuilder: (context, physics){
     return CustomListView.builder(
             itemBuilder: widget.itemBuilder,
             itemCount: widget.itemCount,
             isLoading: _isLoading,
            // controller: _scrollController,
             physics: physics,
             shrinkWrap: widget.shrinkWrap,
             padding: widget.padding,
           );
  });
}
CustomListView的实现

如上,我们需要自定义ListView,它继承系统的ListView,需要传参IndexedWidgetBuilderitemCount和加载中标识isLoading,公司项目中的多绘制一个item,最后一个item作为footer,大家可根据设计自行实现。代码如下:

class CustomListView extends ListView {
  CustomListView.builder({
    required IndexedWidgetBuilder itemBuilder,
    required int itemCount,
    required ValueNotifier<bool> isLoading,
    Key? key,
    ScrollController? controller,
    ScrollPhysics? physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry? padding,
    String? loadingText,
    String? noMoreText,
  }) : super.builder(
            key: key,
            itemCount: itemCount + 1,
            itemBuilder: (context, index) {
              if (index >= itemCount) {
                return ValueListenableBuilder(
                    valueListenable: isLoading, builder: (context, value, child){
                  return isLoading.value == true ? Container(
                    height: 64,
                    alignment: Alignment.center,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Padding(
                          padding: EdgeInsets.all(6),
                          child: CupertinoActivityIndicator(
                            radius: 8,
                          ),
                        ),
                        Text(
                          loadingText ?? “加载中”,
                          style: TextStyle(
                            color: color999999,
                            fontSize: 12,
                          ),
                        ),
                      ],
                    ),
                  ) : Container(
                    height: 64,
                    alignment: Alignment.center,
                    margin: EdgeInsets.only(bottom: 0),
                    child: Text(
                      noMoreText ?? “无更多数据”,
                      style: TextStyle(
                        color: color999999,
                        fontSize: 12,
                      ),
                    ),
                  );
                });
              }
              return itemBuilder(context, index);
            },
            physics: physics,
            shrinkWrap: shrinkWrap,
            padding: padding,
            controller: controller);
}
CustomRefreshListView的使用

使用是很简单的,在onRefresh中请求最新数据,在onLoad中加载更多,在itemBuilder中绘制item。如前文所述,totalCount为可选参数,为后端返回的总item数量,不传则默认为不需要做分页处理。

return CustomRefreshListView(
    onRefresh: () {
      viewModel.loadFirstPage();
    },
    onLoad: () {
      viewModel.loadMore();
    },
    padding: EdgeInsets.only(top: 10.r),
    totalCount: viewModel.riskEntity?.total,
    itemCount: viewModel.listRows.length,
    itemBuilder: (BuildContext context, int index) {
      Model model = viewModel.listModels[index];
      return GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          
        },
        child: _buildItem(viewModel, model),
      );
    });

经过前面几步,我们就封装出了一个简单、易用、低耦合的分页加载列表,大家可根据自己实际项目中的需求修改,全部代码如下:

import 'package:flutter/cupertino.dart';
import 'package:easy_refresh/easy_refresh.dart';
class CustomRefreshListView extends StatefulWidget {
final Function onRefresh;
final Function? onLoad;
final Header? refreshHeader;
final Footer? refreshFooter;
final IndexedWidgetBuilder itemBuilder;
final int itemCount;
final bool shrinkWrap;
final EdgeInsetsGeometry? padding;
final Duration duration;
final String? loadingText;
final String? noMoreText;
final int? totalCount;///分页加载需要
CustomRefreshListView({
Key? key,
this.loadingText,
this.noMoreText,
this.refreshHeader,
this.refreshFooter,
this.onLoad,
this.shrinkWrap = false,
this.padding,
this.duration = const Duration(seconds:2),
required this.totalCount,
required this.onRefresh,
required this.itemCount,
required this.itemBuilder,
}) : super(key: key);
@override
_CustomRefreshListViewState createState() => _CustomRefreshListViewState();
}
class _CustomRefreshListViewState extends State<CustomRefreshListView> {
// final refreshController = EasyRefreshController();
//  final ScrollController _scrollController = ScrollController();
ValueNotifier<bool> _isLoading = ValueNotifier(false);
@override
void dispose() {
super.dispose();
// refreshController.dispose();
// _scrollController.removeListener(_scrollListener);
// _scrollController.dispose();
}
@override
void initState() {
// TODO: implement initState
super.initState();
// _scrollController.addListener(_scrollListener);
}
// void _scrollListener() {
//   if (_scrollController.position.userScrollDirection == ScrollDirection.reverse &&
//       !_isFetching &&
//       _scrollController.position.extentAfter < widget.extentAfter) {
//     _isFetching = true;
//     logcat('接近列表底部,触发事件');
//     if (widget.onLoad != null) {
//       widget.onLoad!();
//     }
//     Future.delayed(Duration(seconds: widget.seconds), () {
//       _isFetching = false; // 重置标志
//     });
//   }
// }
@override
Widget build(BuildContext context) {
return EasyRefresh.builder(
header: widget.refreshHeader  ?? MaterialHeader(),
footer: widget.refreshFooter ?? BuilderFooter(
triggerOffset: 0,
clamping: false,
builder: (BuildContext context, IndicatorState state) {
return SizedBox();
}),
onRefresh: () async {
widget.onRefresh();
},
onLoad: () async{
if (widget.totalCount == null) {
///说明不需要分页加载
print("无需分页加载");
return;
}
if (widget.totalCount != null && widget.totalCount == widget.itemCount) {
///说明全部item加载完毕
print("全部item加载完毕");
return;
}
if (_isLoading.value == false && widget.onLoad != null) {
widget.onLoad!();
_isLoading.value = true;
Future.delayed(widget.duration, () {
_isLoading.value = false; // 重置
});
}
},
childBuilder: (context, physics){
return CustomListView.builder(
itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount,
isLoading: _isLoading,
// controller: _scrollController,
physics: physics,
shrinkWrap: widget.shrinkWrap,
padding: widget.padding,
);
});
}
}
class CustomListView extends ListView {
CustomListView.builder({
required IndexedWidgetBuilder itemBuilder,
required int itemCount,
required ValueNotifier<bool> isLoading,
Key? key,
ScrollController? controller,
ScrollPhysics? physics,
bool shrinkWrap = false,
EdgeInsetsGeometry? padding,
String? loadingText,
String? noMoreText,
}) : super.builder(
key: key,
itemCount: itemCount + 1,
itemBuilder: (context, index) {
if (index >= itemCount) {
return ValueListenableBuilder(
valueListenable: isLoading, builder: (context, value, child){
return isLoading.value == true ? Container(
height: 64,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(6),
child: CupertinoActivityIndicator(
radius: 8,
),
),
Text(
loadingText ?? “加载中”,
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
],
),
) : Container(
height: 64,
alignment: Alignment.center,
margin: EdgeInsets.only(bottom: 0),
child: Text(
noMoreText ?? “无更多数据”,
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
);
});
}
return itemBuilder(context, index);
},
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
controller: controller);
}

原文链接:https://juejin.cn/post/7336530993494310964 作者:卢叁

(0)
上一篇 2024年2月18日 下午4:20
下一篇 2024年2月18日 下午4:31

相关推荐

发表回复

登录后才能评论