上拉刷新下拉加载:SmartRefresher

  1. 简介
  2. 入门案例
    1. 安装
    2. 代码
  3. SmartRefresher
    1. 常用属性
    2. 常用 Header / Footer
    3. RefreshController
      1. 常用属性
      2. 常用方法
  4. RefreshConfiguration
    1. 是什么
    2. 常用属性
    3. 案例

简介

pull_to_refresh_flutter3 是一个用于 Flutter 的第三方包,用来实现 下拉刷新(Pull-to-Refresh)上拉加载更多(Load More) 的功能。它是对早期流行库 pull_to_refresh 的社区维护版本,适配了 Flutter 3。

主要特性:

  • 支持 下拉刷新上拉加载 两种模式
  • 自带多种内置动画 Header / Footer 样式
  • 支持自定义 Header / Footer
  • 支持 ListView / GridView / CustomScrollView 等各种滚动组件

入门案例

安装

flutter pub add pull_to_refresh_flutter3

代码

import 'package:flutter/material.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: RefreshDemoPage());
  }
}

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

  @override
  State<RefreshDemoPage> createState() => _RefreshDemoPageState();
}

class _RefreshDemoPageState extends State<RefreshDemoPage> {
  final RefreshController _refreshController = RefreshController(
    initialRefresh: false,
  );

  List<int> items = List.generate(20, (i) => i);

  /// 下拉刷新
  Future<void> _onRefresh() async {
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      // 模拟数据刷新,这里重新生成了20个数据
      // 实际应用中可以从网络获取最新数据
      items = List.generate(20, (i) => i);
    });
    // 结束刷新状态
    _refreshController.refreshCompleted();
  }

  /// 上拉加载
  Future<void> _onLoading() async {
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      // 模拟加载更多数据,这里在现有数据基础上增加10个数据
      // 实际应用中可以从网络获取更多数据
      items.addAll(List.generate(10, (i) => items.length + i));
    });
    // 结束加载状态
    _refreshController.loadComplete();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('pull_to_refresh_flutter3 示例')),
      // 使用 SmartRefresher 包裹 ListView
      body: SmartRefresher(
        // 绑定控制器
        controller: _refreshController,
        // 启用下拉刷新
        enablePullDown: true,
        // 启用上拉加载
        enablePullUp: true,
        // 下拉刷新和上拉加载的回调
        onRefresh: _onRefresh,
        onLoading: _onLoading,
        // 自定义头部和底部样式
        // WaterDropHeader,内置的水滴样式
        header: const WaterDropHeader(), // 下拉样式
        // ClassicFooter,内置的经典样式
        footer: const ClassicFooter(), // 上拉样式
        // ListView 列表,它可以是任何可滚动组件
        // ListView.builder 是懒加载的,意味着只有可见的部分会被构建
        child: ListView.builder(
          itemCount: items.length,
          itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
        ),
      ),
    );
  }
}

SmartRefresher

常用属性

属性名 类型 说明
controller RefreshController 必填。用于控制刷新 / 加载的状态(开始、结束、重置等)
child Widget 必填。可滚动的内容区域(如 ListView, GridView, CustomScrollView
enablePullDown bool 是否允许下拉刷新,默认 false
enablePullUp bool 是否允许上拉加载更多,默认 false
onRefresh Future<void> Function()? 下拉刷新时触发的回调
onLoading Future<void> Function()? 上拉加载时触发的回调
header RefreshIndicator 自定义下拉刷新头部组件(默认 ClassicHeader
footer LoadIndicator 自定义上拉加载尾部组件(默认 ClassicFooter
physics ScrollPhysics? 设置滚动物理特性,默认 AlwaysScrollableScrollPhysics
scrollController ScrollController? 给内部滚动视图传入 ScrollController
primary bool? ScrollView.primary,是否为主滚动区域
reverse bool 是否反向滚动,默认 false
cacheExtent double? ListView.cacheExtent,控制预加载范围
dragStartBehavior DragStartBehavior 拖拽起始行为,默认 DragStartBehavior.start
keyboardDismissBehavior ScrollViewKeyboardDismissBehavior 控制键盘滚动隐藏行为

内置的 Header:

  • WaterDropHeader()(水滴动画)
  • ClassicHeader()(经典)
  • MaterialClassicHeader()(Material Design 风格)

内置的 Footer:

  • ClassicFooter()(经典)
  • CustomFooter()(可自定义显示内容)

RefreshController

常用属性

属性名 类型 说明
initialRefresh bool 构造参数,是否在初始化时就自动触发一次下拉刷新,默认 false
headerMode ValueNotifier<RefreshStatus> 当前下拉刷新头部状态
footerMode ValueNotifier<LoadStatus> 当前上拉加载尾部状态
position ScrollPosition? 当前绑定的滚动位置(滚动状态)

常用方法

方法名 说明
refreshCompleted() 完成下拉刷新,结束刷新动画
refreshFailed() 下拉刷新失败,显示失败状态
refreshToIdle() 强制让刷新头恢复到初始状态(idle)
requestRefresh({Duration? duration}) 主动触发一次下拉刷新
loadComplete() 完成上拉加载,结束加载动画
loadFailed() 上拉加载失败,显示失败状态
loadNoData() 显示「没有更多数据」状态
resetNoData() 重置「没有更多数据」状态,让上拉加载可以再次触发
dispose() 销毁控制器,一般在 State.dispose() 里调用
final RefreshController _refreshController = RefreshController();

Future<void> _onRefresh() async {
  await Future.delayed(const Duration(seconds: 2));
  // 数据刷新完成后
  _refreshController.refreshCompleted();
}

Future<void> _onLoading() async {
  await Future.delayed(const Duration(seconds: 2));
  // 加载完成
  _refreshController.loadComplete();
  // 如果数据到底了
  // _refreshController.loadNoData();
}

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

RefreshConfiguration

是什么

RefreshConfiguration 是一个 全局配置组件(InheritedWidget),用于:

  • 统一设置 所有 SmartRefresher 的默认行为、动画、阈值等
  • 避免在每个 SmartRefresher 上重复配置

通常把它 包裹在 MaterialApp 的外层或根页面外层

常用属性

属性名 类型 说明
headerBuilder HeaderBuilder? 全局默认下拉刷新头构建器(如 () => WaterDropHeader()
footerBuilder FooterBuilder? 全局默认上拉加载尾构建器(如 () => ClassicFooter()
headerTriggerDistance double 触发下拉刷新的距离阈值,默认 80
footerTriggerDistance double 触发上拉加载的距离阈值(距离底部多少像素开始加载),默认 15.0
maxOverScrollExtent double 最大允许下拉越界距离,默认 100.0
maxUnderScrollExtent double 最大允许上拉越界距离,默认 0.0
springDescription SpringDescription 弹性回弹动画参数(阻尼、质量、刚度)
dragSpeedRatio double 拖拽速度比例,默认 1.0
enableScrollWhenRefreshCompleted bool 刷新完成后是否允许继续滚动,默认 true
enableLoadingWhenNoData bool 没有更多数据时是否还能继续触发加载,默认 false
hideFooterWhenNotFull bool 内容不足一屏时是否隐藏 footer,默认 false
skipCanRefresh bool 是否跳过 canRefresh 状态直接进入 refreshing,默认 false

案例

import 'package:flutter/material.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';

void main() {
  runApp(
    // 全局刷新配置
    RefreshConfiguration(
      headerBuilder: () => const WaterDropHeader(), // 全局默认下拉头
      footerBuilder: () => const ClassicFooter(), // 全局默认上拉尾
      headerTriggerDistance: 80.0, // 下拉触发距离
      footerTriggerDistance: 50.0, // 上拉触发距离
      hideFooterWhenNotFull: false, // 内容不满一屏是否隐藏footer
      enableLoadingWhenNoData: false, // 没有更多数据时禁止继续加载
      child: const MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: RefreshDemoPage());
  }
}

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

  @override
  State<RefreshDemoPage> createState() => _RefreshDemoPageState();
}

class _RefreshDemoPageState extends State<RefreshDemoPage> {
  final RefreshController _refreshController = RefreshController();
  final List<int> _items = List.generate(20, (i) => i);
  int _page = 1;

  // 下拉刷新
  Future<void> _onRefresh() async {
    await Future.delayed(const Duration(seconds: 1));
    setState(() {
      _page = 1;
      _items.clear();
      _items.addAll(List.generate(20, (i) => i));
    });
    _refreshController.refreshCompleted();
    // 重置「没有更多数据」状态,让上拉加载可以再次触发
    // 理解这个方法需要结合 _loadNoData 方法
    _refreshController.resetNoData();
  }

  // 上拉加载
  Future<void> _onLoading() async {
    await Future.delayed(const Duration(seconds: 1));
    if (_page < 3) {
      setState(() {
        _page++;
        // 模拟加载更多数据
        _items.addAll(List.generate(20, (i) => i + _items.length));
      });
      _refreshController.loadComplete();
    } else {
      // 模拟没有更多数据的情况
      // 当确定所有数据都加载完了,就会调用这个方法,这个时候footer 显示「没有更多数据」
      // 并且不会再触发 onLoading 回调,上拉到底部也不会继续加载。
      // 如果想要再次加载,需要调用 resetNoData 重置状态
      _refreshController.loadNoData();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('RefreshConfiguration 示例')),
      body: SmartRefresher(
        controller: _refreshController,
        enablePullDown: true,
        enablePullUp: true,
        onRefresh: _onRefresh,
        onLoading: _onLoading,
        child: ListView.builder(
          itemCount: _items.length,
          itemBuilder: (_, i) => ListTile(title: Text('Item ${_items[i]}')),
        ),
      ),
    );
  }

  @override
  void dispose() {
    // 释放控制器资源
    _refreshController.dispose();
    super.dispose();
  }
}

×

喜欢就点赞,疼爱就打赏