pull_to_refresh_simple

0 阅读5分钟

pull_to_refresh_simple

一个功能强大的 Flutter 下拉刷新和上拉加载更多组件,支持多种自定义指示器样式。

pull_to_refresh_simple

该库修改自github,因原来的库无人维护。

特性

  • ✅ 支持下拉刷新和上拉加载更多
  • ✅ 内置多种指示器样式(Classic、Material、WaterDrop)
  • ✅ 支持完全自定义的 Header 和 Footer
  • ✅ 支持 ListView、GridView、CustomScrollView 等多种滚动组件
  • ✅ 支持全局配置
  • ✅ 支持国际化
  • ✅ 完善的状态管理

安装

pubspec.yaml 文件中添加依赖:

dependencies:
  pull_to_refresh_simple: ^1.0.0

然后运行:

flutter pub get

基本用法

1. 导入包

import 'package:pull_to_refresh_simple/pull_to_refresh.dart';

2. 创建 RefreshController

final RefreshController _refreshController = RefreshController(initialRefresh: false);

3. 使用 SmartRefresher 组件

SmartRefresher(
  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'));
    },
  ),
)

4. 实现刷新和加载回调

void _onRefresh() async {
  // 执行刷新逻辑
  await fetchData();
  // 结束刷新
  _refreshController.refreshCompleted();
}

void _onLoading() async {
  // 执行加载更多逻辑
  await loadMoreData();
  // 结束加载
  _refreshController.loadComplete();
}

5. 释放资源

@override
void dispose() {
  _refreshController.dispose();
  super.dispose();
}

完整示例

import 'package:flutter/material.dart';
import 'package:pull_to_refresh_simple/pull_to_refresh.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final RefreshController _refreshController = RefreshController(initialRefresh: false);
  List<int> _items = List.generate(20, (index) => index);

  void _onRefresh() async {
    await Future.delayed(const Duration(milliseconds: 1500));
    setState(() {
      _items = List.generate(20, (index) => index);
    });
    _refreshController.refreshCompleted();
  }

  void _onLoading() async {
    await Future.delayed(const Duration(milliseconds: 1500));
    setState(() {
      final int length = _items.length;
      _items.addAll(List.generate(20, (index) => length + index));
    });
    _refreshController.loadComplete();
  }

  @override
  void dispose() {
    _refreshController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Pull to Refresh Plus')),
        body: SmartRefresher(
          controller: _refreshController,
          enablePullDown: true,
          enablePullUp: true,
          enableSmartPreload: true, // 启用智能预加载
          onRefresh: _onRefresh,
          onLoading: _onLoading,
          header: const ClassicHeader(),
          footer: const ClassicFooter(),
          child: ListView.builder(
            padding: const EdgeInsets.all(10),
            itemCount: _items.length,
            itemBuilder: (context, index) {
              return Container(
                height: 100,
                margin: const EdgeInsets.only(bottom: 10),
                color: Colors.grey,
                child: Center(
                  child: Text(
                    'Item ${_items[index]}',
                    style: const TextStyle(color: Colors.white, fontSize: 16),
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

内置指示器

Header 指示器

1. ClassicHeader

经典下拉刷新指示器,支持自定义文本和图标。

SmartRefresher(
  header: const ClassicHeader(
    idleText: '下拉刷新',
    releaseText: '释放刷新',
    refreshingText: '刷新中...',
    completeText: '刷新完成',
    failedText: '刷新失败',
  ),
  // ...
)
2. MaterialClassicHeader

Material Design 风格的刷新指示器。

SmartRefresher(
  header: const MaterialClassicHeader(
    color: Colors.blue,
    backgroundColor: Colors.white,
    distance: 50.0,
  ),
  // ...
)
3. WaterDropMaterialHeader

带水滴效果的 Material 风格指示器。

SmartRefresher(
  header: const WaterDropMaterialHeader(
    color: Colors.white,
    backgroundColor: Colors.blue,
    distance: 60.0,
  ),
  // ...
)

Footer 指示器

ClassicFooter

经典上拉加载指示器。

SmartRefresher(
  footer: const ClassicFooter(
    idleText: '上拉加载',
    loadingText: '加载中...',
    noDataText: '没有更多数据',
    failedText: '加载失败',
    canLoadingText: '释放加载',
  ),
  // ...
)

自定义指示器

自定义 Header

使用 CustomHeader 创建完全自定义的下拉刷新指示器:

class MyCustomHeader extends StatefulWidget {
  const MyCustomHeader({super.key});

  @override
  State<MyCustomHeader> createState() => _MyCustomHeaderState();
}

class _MyCustomHeaderState extends State<MyCustomHeader> {
  @override
  Widget build(BuildContext context) {
    return CustomHeader(
      refreshStyle: RefreshStyle.follow,
      height: 100,
      onOffsetChange: (offset) {
        // 偏移量变化回调
      },
      onModeChange: (mode) {
        // 状态变化回调
      },
      builder: (context, mode) {
        // 根据不同状态返回不同的 UI
        if (mode == RefreshStatus.refreshing) {
          return const CircularProgressIndicator();
        } else if (mode == RefreshStatus.completed) {
          return const Text('刷新完成');
        } else {
          return const Text('下拉刷新');
        }
      },
    );
  }
}

自定义 Footer

使用 CustomFooter 创建完全自定义的上拉加载指示器:

class MyCustomFooter extends StatefulWidget {
  const MyCustomFooter({super.key});

  @override
  State<MyCustomFooter> createState() => _MyCustomFooterState();
}

class _MyCustomFooterState extends State<MyCustomFooter> {
  @override
  Widget build(BuildContext context) {
    return CustomFooter(
      height: 50,
      onModeChange: (mode) {
        // 状态变化回调
      },
      builder: (context, mode) {
        if (mode == LoadStatus.loading) {
          return const CircularProgressIndicator();
        } else if (mode == LoadStatus.noMore) {
          return const Text('没有更多数据');
        } else {
          return const Text('上拉加载');
        }
      },
    );
  }
}

SmartRefresher 参数说明

参数类型默认值说明
controllerRefreshController-控制刷新和加载状态的控制器,不能为空
childWidget?-刷新内容组件
headerWidget?-头部刷新指示器
footerWidget?-底部加载更多指示器
enablePullDownbooltrue是否启用下拉刷新功能
enablePullUpboolfalse是否启用上拉加载更多功能
enableSmartPreloadbool?null是否启用智能预加载(基于上一次加载高度的一半进行预加载),默认使用全局配置
onRefreshVoidCallback?-下拉刷新回调
onLoadingVoidCallback?-上拉加载更多回调
scrollDirectionAxis?-滚动方向(复制自 ScrollView)
reversebool?-是否反向滚动(复制自 ScrollView)
scrollControllerScrollController?-滚动控制器(复制自 ScrollView)
primarybool?-是否使用 primary scroll controller(复制自 ScrollView)
physicsScrollPhysics?-滚动物理效果(复制自 ScrollView)
cacheExtentdouble?-缓存区域大小(复制自 ScrollView)
semanticChildCountint?-语义化子组件数量(复制自 ScrollView)
dragStartBehaviorDragStartBehavior?-拖动开始行为(复制自 ScrollView)

RefreshController API

刷新相关方法

// 手动触发刷新
_refreshController.requestRefresh();

// 刷新成功
_refreshController.refreshCompleted();

// 刷新失败
_refreshController.refreshFailed();

// 直接结束刷新(不显示成功或失败状态)
_refreshController.refreshToIdle();

加载相关方法

// 手动触发加载
_refreshController.requestLoading();

// 加载成功
_refreshController.loadComplete();

// 加载失败
_refreshController.loadFailed();

// 没有更多数据
_refreshController.loadNoData();

// 重置无数据状态
_refreshController.resetNoData();

状态属性

// 当前头部状态
RefreshStatus? status = _refreshController.headerStatus;

// 当前底部状态
LoadStatus? status = _refreshController.footerStatus;

// 是否正在刷新
bool isRefreshing = _refreshController.isRefresh;

// 是否正在加载
bool isLoading = _refreshController.isLoading;

全局配置

使用 RefreshConfiguration 为子树中的所有 SmartRefresher 提供全局配置:

RefreshConfiguration(
  headerBuilder: () => const ClassicHeader(),
  footerBuilder: () => const ClassicFooter(),
  headerTriggerDistance: 80.0,
  footerTriggerDistance: 15.0,
  springDescription: const SpringDescription(
    mass: 1.0,
    stiffness: 364.72,
    damping: 35.2,
  ),
  enableScrollWhenRefreshCompleted: true,
  enableLoadingWhenFailed: true,
  enableBallisticLoad: true,
  child: MaterialApp(
    home: YourHomePage(),
  ),
)

配置参数说明

参数类型默认值说明
headerBuilderIndicatorBuilder?-全局默认 Header 构建器
footerBuilderIndicatorBuilder?-全局默认 Footer 构建器
headerTriggerDistancedouble80.0触发刷新的距离
footerTriggerDistancedouble15.0触发加载的距离
dragSpeedRatiodouble1.0拖动速度比例
maxOverScrollExtentdouble?-最大过度滚动距离
maxUnderScrollExtentdouble?-最大不足滚动距离
enableScrollWhenRefreshCompletedboolfalse刷新完成回弹时是否允许滚动
enableLoadingWhenFailedbooltrue失败状态下是否允许加载
enableLoadingWhenNoDataboolfalse无数据状态下是否允许加载
enableBallisticLoadbooltrue是否通过 BallisticScrollActivity 触发加载
enableSmartPreloadbooltrue是否启用智能预加载
hideFooterWhenNotFullboolfalse内容不满一页时是否隐藏 Footer

状态枚举

RefreshStatus(刷新状态)

状态说明
idle初始状态,未被拖动
canRefresh拖动距离足够,释放后将触发刷新
refreshing刷新中
completed刷新完成
failed刷新失败

LoadStatus(加载状态)

状态说明
idle初始状态,可以触发加载
canLoading拖动距离足够,释放后将触发加载
loading加载中
noMore没有更多数据
failed加载失败

刷新样式

RefreshStyle(Header 显示样式)

样式说明
follow指示器始终跟随内容移动
unFollow指示器跟随内容移动,到达顶部后不再跟随
behind指示器大小随边界距离缩放,显示在内容后面
front类似 Flutter 原生 RefreshIndicator,显示在内容上方

LoadStyle(Footer 显示样式)

样式说明
showAlways始终占据布局范围
hideAlways始终不占据布局范围
showWhenLoading仅在加载状态下占据布局范围

注意事项

  1. 必须结束状态:在 onRefreshonLoading 回调中,必须调用相应的结束方法(如 refreshCompleted()loadComplete()),否则会一直保持刷新或加载状态。

  2. Controller 生命周期:记得在 dispose() 方法中调用 _refreshController.dispose() 释放资源。

  3. 一个 Controller 对应一个 SmartRefresher:不要将同一个 RefreshController 用于多个 SmartRefresher,这会导致意外错误。

  4. 避免嵌套滚动:如果 child 内部包含可滚动组件,建议使用 CustomScrollView 或 SmartRefresher.builder 构造函数。

常见问题

1. 刷新/加载状态无法结束?

确保在 onRefreshonLoading 回调中调用了相应的结束方法。

2. 如何禁用下拉刷新或上拉加载?

设置 enablePullDown: falseenablePullUp: false

3. 如何手动触发刷新?

调用 _refreshController.requestRefresh() 方法。

4. 如何显示"没有更多数据"?

调用 _refreshController.loadNoData() 方法。

5. 如何重置"没有更多数据"状态?

调用 _refreshController.resetNoData() 方法。