Pullable
简介
Pullable 组件为任何可滚动内容添加下拉刷新和加载更多功能。它使用手势驱动的刷新和分页行为包裹你的子组件,并支持多种头部动画样式。
基于 pull_to_refresh_flutter3 包构建,Pullable 提供了简洁的 API,并为常见配置提供了命名构造函数。
Pullable(
onRefresh: () async {
// Fetch fresh data
await fetchData();
},
child: ListView(
children: items.map((item) => ListTile(title: Text(item))).toList(),
),
)
基本用法
用 Pullable 包裹任何可滚动组件:
Pullable(
onRefresh: () async {
await loadLatestPosts();
},
child: ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) => PostCard(post: posts[index]),
),
)
当用户在列表上下拉时,onRefresh 回调将被触发。刷新指示器在回调完成后自动结束。
构造函数
Pullable 为常见配置提供了命名构造函数:
| 构造函数 | 头部样式 | 描述 |
|---|---|---|
Pullable() |
Water Drop | 默认构造函数 |
Pullable.classicHeader() |
Classic | 经典下拉刷新样式 |
Pullable.waterDropHeader() |
Water Drop | 水滴动画 |
Pullable.materialClassicHeader() |
Material Classic | Material Design 经典样式 |
Pullable.waterDropMaterialHeader() |
Water Drop Material | Material 水滴样式 |
Pullable.bezierHeader() |
Bezier | 贝塞尔曲线动画 |
Pullable.noBounce() |
可配置 | 使用 ClampingScrollPhysics 减少弹跳效果 |
Pullable.custom() |
自定义组件 | 使用你自己的头部/底部组件 |
Pullable.builder() |
可配置 | 完全控制 PullableConfig |
示例
// Classic header
Pullable.classicHeader(
onRefresh: () async => await refreshData(),
child: myListView,
)
// Material header
Pullable.materialClassicHeader(
onRefresh: () async => await refreshData(),
child: myListView,
)
// No bounce effect
Pullable.noBounce(
onRefresh: () async => await refreshData(),
headerType: PullableHeaderType.classic,
child: myListView,
)
// Custom header widget
Pullable.custom(
customHeader: MyCustomRefreshHeader(),
onRefresh: () async => await refreshData(),
child: myListView,
)
PullableConfig
如需精细控制,请将 PullableConfig 与 Pullable.builder() 构造函数配合使用:
Pullable.builder(
config: PullableConfig(
enablePullDown: true,
enablePullUp: true,
headerType: PullableHeaderType.materialClassic,
onRefresh: () async => await refreshData(),
onLoading: () async => await loadMoreData(),
refreshCompleteDelay: Duration(milliseconds: 500),
loadCompleteDelay: Duration(milliseconds: 300),
physics: BouncingScrollPhysics(),
),
child: myListView,
)
所有配置选项
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
enablePullDown |
bool |
true |
启用下拉刷新 |
enablePullUp |
bool |
false |
启用上拉加载更多 |
physics |
ScrollPhysics? |
null | 自定义滚动物理效果 |
onRefresh |
Future<void> Function()? |
null | 刷新回调 |
onLoading |
Future<void> Function()? |
null | 加载更多回调 |
headerType |
PullableHeaderType |
waterDrop |
头部动画样式 |
customHeader |
Widget? |
null | 自定义头部组件 |
customFooter |
Widget? |
null | 自定义底部组件 |
refreshCompleteDelay |
Duration |
Duration.zero |
刷新完成前的延迟 |
loadCompleteDelay |
Duration |
Duration.zero |
加载完成前的延迟 |
enableOverScroll |
bool |
true |
允许过度滚动效果 |
cacheExtent |
double? |
null | 滚动缓存范围 |
semanticChildCount |
int? |
null | 语义子项数量 |
dragStartBehavior |
DragStartBehavior |
start |
拖拽手势的开始方式 |
头部样式
从五种内置头部动画中选择:
enum PullableHeaderType {
classic, // Classic pull indicator
waterDrop, // Water drop animation (default)
materialClassic, // Material Design classic
waterDropMaterial, // Material water drop
bezier, // Bezier curve animation
}
通过构造函数或配置设置样式:
// Via named constructor
Pullable.bezierHeader(
onRefresh: () async => await refreshData(),
child: myListView,
)
// Via config
Pullable.builder(
config: PullableConfig(
headerType: PullableHeaderType.bezier,
onRefresh: () async => await refreshData(),
),
child: myListView,
)
上拉加载更多
启用上拉加载实现分页:
Pullable.builder(
config: PullableConfig(
enablePullDown: true,
enablePullUp: true,
onRefresh: () async {
// Reset to page 1
page = 1;
items = await fetchItems(page: page);
setState(() {});
},
onLoading: () async {
// Load next page
page++;
List<Item> more = await fetchItems(page: page);
items.addAll(more);
setState(() {});
},
),
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ItemTile(item: items[index]),
),
)
自定义头部和底部
提供你自己的头部和底部组件:
Pullable.custom(
customHeader: Container(
height: 60,
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
customFooter: Container(
height: 40,
alignment: Alignment.center,
child: Text("Loading more..."),
),
enablePullUp: true,
onRefresh: () async => await refreshData(),
onLoading: () async => await loadMore(),
child: myListView,
)
控制器
使用 RefreshController 进行编程式控制:
final RefreshController _controller = RefreshController();
Pullable(
controller: _controller,
onRefresh: () async => await refreshData(),
child: myListView,
)
// Trigger refresh programmatically
_controller.triggerRefresh();
// Trigger loading programmatically
_controller.triggerLoading();
// Check state
bool refreshing = _controller.isRefreshing;
bool loading = _controller.isLoading;
RefreshController 的扩展方法
| 方法/属性 | 返回类型 | 描述 |
|---|---|---|
triggerRefresh() |
void |
手动触发刷新 |
triggerLoading() |
void |
手动触发加载更多 |
isRefreshing |
bool |
是否正在刷新 |
isLoading |
bool |
是否正在加载 |
扩展方法
任何组件都可以使用 .pullable() 扩展方法来包裹下拉刷新功能:
ListView(
children: items.map((item) => ListTile(title: Text(item.name))).toList(),
).pullable(
onRefresh: () async {
await fetchItems();
},
)
使用自定义配置:
myListView.pullable(
onRefresh: () async => await refreshData(),
pullableConfig: PullableConfig(
headerType: PullableHeaderType.classic,
enablePullUp: true,
onLoading: () async => await loadMore(),
),
)
CollectionView 集成
CollectionView 提供内置分页的 pullable 变体:
CollectionView.pullable
CollectionView<User>.pullable(
data: (iteration) async => api.getUsers(page: iteration),
builder: (context, item) => UserTile(user: item.data),
onRefresh: () => print('Refreshed!'),
headerStyle: 'WaterDropHeader',
)
CollectionView.pullableSeparated
CollectionView<User>.pullableSeparated(
data: (iteration) async => api.getUsers(page: iteration),
builder: (context, item) => UserTile(user: item.data),
separatorBuilder: (context, index) => Divider(),
)
CollectionView.pullableGrid
CollectionView<Product>.pullableGrid(
data: (iteration) async => api.getProducts(page: iteration),
builder: (context, item) => ProductCard(product: item.data),
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
)
Pullable 专用参数
| 参数 | 类型 | 描述 |
|---|---|---|
data |
Function(int iteration) |
分页数据回调(iteration 从 1 开始) |
onRefresh |
Function()? |
刷新后的回调 |
beforeRefresh |
Function()? |
刷新开始前的钩子 |
afterRefresh |
Function(dynamic)? |
刷新后带数据的钩子 |
headerStyle |
String? |
头部类型名称(例如 'WaterDropHeader'、'ClassicHeader') |
footerLoadingIcon |
Widget? |
底部的自定义加载指示器 |
示例
带刷新的分页列表
class _PostListState extends NyState<PostListPage> {
List<Post> posts = [];
int page = 1;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Pullable.builder(
config: PullableConfig(
enablePullDown: true,
enablePullUp: true,
headerType: PullableHeaderType.materialClassic,
onRefresh: () async {
page = 1;
posts = await api<PostApiService>((request) => request.getPosts(page: page));
setState(() {});
},
onLoading: () async {
page++;
List<Post> more = await api<PostApiService>((request) => request.getPosts(page: page));
posts.addAll(more);
setState(() {});
},
),
child: ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) => PostCard(post: posts[index]),
),
),
);
}
}
使用扩展方法的简单刷新
ListView(
children: notifications
.map((n) => ListTile(
title: Text(n.title),
subtitle: Text(n.body),
))
.toList(),
).pullable(
onRefresh: () async {
notifications = await fetchNotifications();
setState(() {});
},
)