my_refresh_list.dart 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. /// 封装下拉刷新与加载更多
  4. class DeerListView extends StatefulWidget {
  5. const DeerListView({
  6. super.key,
  7. required this.itemCount,
  8. required this.itemBuilder,
  9. required this.onRefresh,
  10. this.loadMore,
  11. this.hasMore = false,
  12. this.padding,
  13. this.controller,
  14. });
  15. final RefreshCallback onRefresh;
  16. final LoadMoreCallback? loadMore;
  17. final int itemCount;
  18. final bool hasMore;
  19. final IndexedWidgetBuilder itemBuilder;
  20. final ScrollController? controller;
  21. /// padding属性使用时注意会破坏原有的SafeArea,需要自行计算bottom大小
  22. final EdgeInsetsGeometry? padding;
  23. @override
  24. State<StatefulWidget> createState() => _DeerListViewState();
  25. }
  26. typedef RefreshCallback = Future<void> Function();
  27. typedef LoadMoreCallback = Future<void> Function();
  28. class _DeerListViewState extends State<DeerListView> {
  29. /// 是否正在加载数据
  30. bool _isLoading = false;
  31. @override
  32. Widget build(BuildContext context) {
  33. final Widget child = RefreshIndicator(
  34. onRefresh: widget.onRefresh,
  35. child:
  36. // widget.itemCount == 0 ?
  37. // StateLayout(type: widget.stateType) :
  38. ListView.separated(
  39. controller: widget.controller,
  40. itemCount:
  41. widget.loadMore == null ? widget.itemCount : widget.itemCount + 1,
  42. padding: widget.padding,
  43. itemBuilder: (BuildContext context, int index) {
  44. /// 不需要加载更多则不需要添加FootView
  45. if (widget.loadMore == null) {
  46. return widget.itemBuilder(context, index);
  47. } else {
  48. return index < widget.itemCount
  49. ? widget.itemBuilder(context, index)
  50. : MoreWidget(widget.itemCount, widget.hasMore);
  51. }
  52. },
  53. separatorBuilder: (context, index) {
  54. return const Divider();
  55. },
  56. ),
  57. );
  58. return SafeArea(
  59. child: NotificationListener<ScrollNotification>(
  60. onNotification: (ScrollNotification note) {
  61. /// 确保是垂直方向滚动,且滑动至底部
  62. if (note.metrics.pixels == note.metrics.maxScrollExtent &&
  63. note.metrics.axis == Axis.vertical) {
  64. _loadMore();
  65. }
  66. return true;
  67. },
  68. child: child,
  69. ),
  70. );
  71. }
  72. Future<void> _loadMore() async {
  73. if (widget.loadMore == null) {
  74. return;
  75. }
  76. if (_isLoading) {
  77. return;
  78. }
  79. if (!widget.hasMore) {
  80. return;
  81. }
  82. _isLoading = true;
  83. await widget.loadMore?.call();
  84. _isLoading = false;
  85. }
  86. }
  87. class MoreWidget extends StatelessWidget {
  88. const MoreWidget(this.itemCount, this.hasMore, {super.key});
  89. final int itemCount;
  90. final bool hasMore;
  91. @override
  92. Widget build(BuildContext context) {
  93. TextStyle style = const TextStyle(color: Color(0x8A000000));
  94. return Padding(
  95. padding: const EdgeInsets.symmetric(vertical: 10.0),
  96. child: Row(
  97. mainAxisAlignment: MainAxisAlignment.center,
  98. children: <Widget>[
  99. if (hasMore) const CupertinoActivityIndicator(),
  100. Text(hasMore ? '正在加载中...' : '没有了呦~', style: style),
  101. ],
  102. ),
  103. );
  104. }
  105. }