一个功能强大的 Flutter 下拉刷新和上拉加载更多组件,支持多种自定义指示器样式。
一、库的整体结构
首先,让我们了解一下这个库的文件结构:
bash
lib/
├── pull_to_refresh_simple.dart # 入口文件,导出核心组件
└── src/
├── smart_refresher.dart # 核心组件 SmartRefresher
├── indicator/ # 指示器相关组件
│ ├── classic_indicator.dart # 经典指示器
│ ├── custom_indicator.dart # 自定义指示器
│ └── material_indicator.dart # Material风格指示器
└── internals/ # 内部实现
├── indicator_wrap.dart # 指示器包装和状态管理
├── refresh_localizations.dart # 国际化支持
├── refresh_physics.dart # 滚动物理效果
└── slivers.dart # 自定义Sliver组件
二、核心组件设计
1. SmartRefresher - 核心容器组件
SmartRefresher 是整个库的核心组件,负责整合下拉刷新和上拉加载功能。
设计思路:
- 采用
StatefulWidget设计,管理内部状态 - 支持两种构造方式:普通构造和
builder构造 - 提供丰富的配置参数,如
enablePullDown、enablePullUp、enableSmartPreload等 - 通过
RefreshController控制刷新和加载状态
关键代码:
dart
class SmartRefresher extends StatefulWidget {
// 核心参数
final Widget? child;
final Widget? header;
final Widget? footer;
final bool enablePullUp;
final bool enablePullDown;
final VoidCallback? onRefresh;
final VoidCallback? onLoading;
final RefreshController controller;
final bool? enableSmartPreload; // 我们新增的智能预加载参数
// 构造函数
const SmartRefresher({
super.key,
required this.controller,
this.child,
this.header,
this.footer,
this.enablePullDown = true,
this.enablePullUp = false,
this.onRefresh,
this.onLoading,
this.enableSmartPreload,
// 其他参数...
});
// builder构造函数
const SmartRefresher.builder({
super.key,
required this.controller,
required this.builder,
this.enablePullDown = true,
this.enablePullUp = false,
this.onRefresh,
this.onLoading,
this.enableSmartPreload,
});
}
2. RefreshController - 状态控制器
RefreshController 负责控制刷新和加载的状态,是连接 UI 和业务逻辑的桥梁。
设计思路:
- 管理头部和底部的状态(通过
headerMode和footerMode) - 提供手动触发刷新和加载的方法
- 提供结束刷新和加载的方法
- 监听滚动位置,用于触发预加载等功能
关键代码:
dart
class RefreshController {
// 状态通知器
RefreshNotifier<RefreshStatus>? headerMode;
RefreshNotifier<LoadStatus>? footerMode;
// 滚动位置
ScrollPosition? position;
// 方法:触发刷新
Future<void>? requestRefresh({...}) {
// 实现逻辑...
}
// 方法:结束刷新
void refreshCompleted({bool resetFooterState = false}) {
headerMode?.value = RefreshStatus.completed;
// 其他逻辑...
}
// 方法:触发加载
Future<void>? requestLoading({...}) {
// 实现逻辑...
}
// 方法:结束加载
void loadComplete() {
WidgetsBinding.instance.addPostFrameCallback((_) {
footerMode?.value = LoadStatus.idle;
});
}
}
3. 指示器系统
库提供了多种指示器样式,包括经典样式、Material 样式等。
设计思路:
- 抽象出
RefreshIndicator和LoadIndicator基类 - 通过
IndicatorStateMixin管理指示器的通用状态和行为 - 提供
CustomHeader和CustomFooter支持完全自定义
关键代码:
dart
// 刷新指示器基类
abstract class RefreshIndicator extends StatefulWidget {
final RefreshStyle refreshStyle;
final double height;
// 其他参数...
}
// 加载指示器基类
abstract class LoadIndicator extends StatefulWidget {
final LoadStyle loadStyle;
final double height;
// 其他参数...
}
// 指示器状态混合类
mixin IndicatorStateMixin<T extends StatefulWidget, V> on State<T> {
SmartRefresher? refresher;
RefreshConfiguration? configuration;
SmartRefresherState? refresherState;
// 其他属性和方法...
}
三、核心实现逻辑
1. 下拉刷新实现
工作原理:
- 用户下拉时,通过
RefreshPhysics计算偏移量 - 当偏移量达到触发距离时,进入
canRefresh状态 - 用户释放后,进入
refreshing状态并调用onRefresh回调 - 刷新完成后,调用
refreshCompleted结束刷新状态
关键代码:
dart
// 处理主动滚动状态
void _handleActiveScroll(double offset) {
// 处理下拉刷新
if (refresher!.enablePullDown) {
if (offset >= configuration!.headerTriggerDistance) {
if (!configuration!.skipCanRefresh) {
mode = RefreshStatus.canRefresh;
} else {
_enterRefreshingState();
}
} else {
mode = RefreshStatus.idle;
}
}
}
// 进入刷新状态
void _enterRefreshingState() {
floating = true;
update();
readyToRefresh().then((_) {
if (mounted) {
mode = RefreshStatus.refreshing;
}
});
}
2. 上拉加载实现
工作原理:
- 用户上拉时,通过
_checkIfCanLoading方法检查是否需要加载 - 当满足加载条件时,进入
canLoading状态 - 用户释放后,进入
loading状态并调用onLoading回调 - 加载完成后,调用
loadComplete结束加载状态
关键代码:
dart
// 检查是否可以加载更多
bool _checkIfCanLoading() {
// 失败状态下不允许加载
if (!configuration!.enableLoadingWhenFailed && mode == LoadStatus.failed) {
return false;
}
// 没有更多数据时不允许加载
if (!configuration!.enableLoadingWhenNoData && mode == LoadStatus.noMore) {
return false;
}
// 智能预加载:当距离底部小于上一次加载高度的一半时触发
final bool enableSmartPreload = refresher?.enableSmartPreload ?? configuration!.enableSmartPreload;
if (enableSmartPreload &&
lastLoadedHeight > 0 &&
_position!.maxScrollExtent - _position!.pixels < (lastLoadedHeight * 0.5) &&
_enableLoading) {
return true;
}
// 传统预加载:当距离底部小于触发距离时触发
if (_position!.maxScrollExtent - _position!.pixels <= configuration!.footerTriggerDistance &&
_position!.extentBefore > 2.0 &&
_enableLoading) {
return true;
}
return false;
}
3. 智能预加载实现
工作原理:
- 记录上一次加载的数据高度(
lastLoadedHeight) - 当用户滚动到距离底部还有上一次加载高度的一半时
- 自动触发加载更多操作,无需用户手动上拉到底部
关键代码:
dart
// 智能预加载判断
final bool enableSmartPreload = refresher?.enableSmartPreload ?? configuration!.enableSmartPreload;
if (enableSmartPreload &&
lastLoadedHeight > 0 &&
_position!.maxScrollExtent - _position!.pixels < (lastLoadedHeight * 0.5) &&
_enableLoading) {
return true;
}
4. 滚动物理效果实现
工作原理:
- 继承
ScrollPhysics实现自定义的RefreshPhysics - 重写相关方法,处理下拉和上拉的物理效果
- 根据配置参数调整滚动行为
关键代码:
dart
// 获取滚动物理效果
ScrollPhysics _getScrollPhysics(RefreshConfiguration? conf, ScrollPhysics physics) {
// 判断是否使用弹性物理效果
final bool isBouncingPhysics = physics is BouncingScrollPhysics ||
(physics is AlwaysScrollableScrollPhysics &&
ScrollConfiguration.of(context).getScrollPhysics(context).runtimeType == BouncingScrollPhysics);
return _physics = RefreshPhysics(
dragSpeedRatio: conf?.dragSpeedRatio ?? 1,
springDescription: conf?.springDescription ??
const SpringDescription(
mass: 1.0,
stiffness: 364.72,
damping: 35.2,
),
controller: widget.controller,
updateFlag: _updatePhysics ? 0 : 1,
enableScrollWhenRefreshCompleted: conf?.enableScrollWhenRefreshCompleted ?? false,
maxUnderScrollExtent: conf?.maxUnderScrollExtent ?? (isBouncingPhysics ? double.infinity : 0.0),
maxOverScrollExtent: conf?.maxOverScrollExtent ?? (isBouncingPhysics ? double.infinity : 60.0),
topHitBoundary:
conf?.topHitBoundary ?? (isBouncingPhysics ? double.infinity : 0.0),
bottomHitBoundary: conf?.bottomHitBoundary ?? (isBouncingPhysics ? double.infinity : 0.0))
.applyTo(!_canDrag ? const NeverScrollableScrollPhysics() : physics);
}
四、组件渲染流程
-
初始化:
- 创建
SmartRefresher实例,传入controller和其他参数 initState中绑定控制器状态,设置初始刷新(如果需要)
- 创建
-
构建过程:
build方法中获取RefreshConfiguration- 根据
builder或child构建内容 - 构建
CustomScrollView或包装现有Scrollable - 添加 header 和 footer 指示器
-
状态管理:
- 通过
RefreshController管理刷新和加载状态 - 状态变化时,通过
RefreshNotifier通知 UI 更新 - 滚动时,通过
_handleOffsetChange方法处理偏移量变化
- 通过
-
事件处理:
- 监听滚动事件,计算偏移量
- 根据偏移量和状态触发相应的回调
- 处理用户交互,如下拉、上拉等
五、使用流程
-
创建控制器:
dartfinal RefreshController _refreshController = RefreshController(initialRefresh: false); -
使用 SmartRefresher:
dartSmartRefresher( controller: _refreshController, enablePullDown: true, enablePullUp: true, enableSmartPreload: true, onRefresh: _onRefresh, onLoading: _onLoading, header: const ClassicHeader(), footer: const ClassicFooter(), child: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ListTile(title: Text('Item $index')); }, ), ) -
实现回调:
dartvoid _onRefresh() async { // 执行刷新逻辑 await fetchData(); // 结束刷新 _refreshController.refreshCompleted(); } void _onLoading() async { // 执行加载更多逻辑 await loadMoreData(); // 结束加载 _refreshController.loadComplete(); } -
释放资源:
dart@override void dispose() { _refreshController.dispose(); super.dispose(); }
七、总结
pull_to_refresh_plus 库提供了一个功能强大、灵活易用的下拉刷新和上拉加载解决方案。它的核心设计思路是:
- 分层设计:将核心逻辑、UI 表现和状态管理分离
- 可扩展性:通过抽象类和混合类支持自定义
- 用户体验:通过智能预加载等功能提升用户体验
- 灵活性:提供丰富的配置选项,适应不同场景
对于 Flutter 初级工程师来说,理解这个库的设计和实现,可以帮助你更好地掌握 Flutter 的状态管理、滚动机制和自定义组件开发。同时,这个库也是学习如何构建一个功能完整、用户体验良好的Flutter 组件的优秀范例。