Flutter下拉刷新上拉加载的简单实现方式二

一个简单的Flutter应用程序,展示了如何实现下拉刷新和上拉加载更多的功能。

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

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

  @override
  MyRefreshDemoPageState createState() => MyRefreshDemoPageState();
}

class MyRefreshDemoPageState extends State<MyRefreshDemoPage> {
  final int pageSize = 30;

  bool disposed = false;

  List<String> dataList = [];

  final ScrollController _scrollController = ScrollController();

  Future<void> onRefresh() async {
    await Future.delayed(const Duration(seconds: 2));
    dataList.clear();
    for (int i = 0; i < pageSize; i++) {
      dataList.add("refresh");
    }
    if (disposed) {
      return;
    }
    setState(() {});
  }

  Future<void> loadMore() async {
    await Future.delayed(const Duration(seconds: 2));
    for (int i = 0; i < pageSize; i++) {
      dataList.add("loadmore");
    }
    if (disposed) {
      return;
    }
    setState(() {});
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    Future.delayed(const Duration(milliseconds: 500), () {
      _scrollController.animateTo(-150,
          duration: const Duration(milliseconds: 600), curve: Curves.linear);
      return true;
    });
  }

  @override
  void dispose() {
    disposed = true;
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("MyRefreshDemoPage"),
      ),
      body: NotificationListener(
        onNotification: (ScrollNotification notification) {
          //判断是否满足触发加载更多的条件
          if (notification is ScrollEndNotification) {
            if (_scrollController.position.pixels > 0 &&
                _scrollController.position.pixels ==
                    _scrollController.position.maxScrollExtent) {
              loadMore();
            }
          }
          return false;
        },
        child: CustomScrollView(
          controller: _scrollController,

          //回弹效果
          physics: const BouncingScrollPhysics(
              parent: AlwaysScrollableScrollPhysics()),
          slivers: <Widget>[
            //控制显示刷新的CupertinoSliverRefreshControl
            CupertinoSliverRefreshControl(
              refreshIndicatorExtent: 100,
              refreshTriggerPullDistance: 140,
              onRefresh: onRefresh,
            ),
            SliverSafeArea(
              sliver: SliverList(
                //代理显示
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                    if (index == dataList.length) {
                      return Container(
                        margin: const EdgeInsets.all(10),
                        child: const Align(
                          child: CircularProgressIndicator(),
                        ),
                      );
                    }
                    return Card(
                      child: Container(
                        height: 60,
                        alignment: Alignment.centerLeft,
                        child: Text("Item ${dataList[index]} $index"),
                      ),
                    );
                  },
                  childCount: (dataList.length >= pageSize)
                      ? dataList.length + 1
                      : dataList.length,
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

类结构

`MyRefreshDemoPage`:

  • 这是一个`StatefulWidget`,意味着它有状态并且可以动态更新界面。
  • 其对应的状态类是`MyRefreshDemoPageState`。

`MyRefreshDemoPageState`:

  • 包含应用程序的状态逻辑和界面构建。
  • 使用`ScrollController`来监听滚动事件。
  • 定义了两个主要的异步方法:`onRefresh`和`loadMore`,分别用于处理刷新和加载更多的数据。

相关代码分析

CupertinoSliverRefreshControl

  • 通过`CupertinoSliverRefreshControl`实现下拉刷新。
  • `onRefresh`方法模拟了一个2秒的延迟,然后清空数据列表并添加新的数据项,最后调用`setState`更新界面。

`CupertinoSliverRefreshControl` 是 Flutter 提供的一个用于实现下拉刷新控件的 Cupertino 风格组件,适用于 iOS 风格的应用。它通常用于 `CustomScrollView` 中,以实现类似于 iOS 的下拉刷新效果。

基本特性

  • 样式:模仿 iOS 的下拉刷新样式,与 `Cupertino` 系列组件一致,提供了典型的流畅过渡和回弹效果。
  • 集成:通常与 `CustomScrollView` 和其他 `Sliver` 组件一起使用。
主要属性
1.`refreshTriggerPullDistance`:
  • 是触发刷新的拉动距离。
  • 当用户向下拉动的距离超过这个值时,`onRefresh` 回调函数将被触发。
2.`refreshIndicatorExtent`:
  • 是刷新指示器显示的最大距离。
  • 在用户拉动超过 `refreshTriggerPullDistance` 后,指示器将扩展到这个距离。
3.`onRefresh`:
  • 类型为 `Future Function()` 的回调函数。
  • 当下拉操作触发刷新时,该函数被调用。
  • 通常在此函数中执行异步操作(如获取数据),完成后刷新指示器会自动收回。
注意事项
  • 物理滚动效果:通常与 `BouncingScrollPhysics` 一起使用,以获得更好的用户体验。
  • 兼容性:`CupertinoSliverRefreshControl` 主要用于 iOS 风格的应用,在 Android 平台上建议使用 `RefreshIndicator` 来保持风格一致。
  • 异步刷新处理:确保 `onRefresh` 中的异步操作完成后调用 `setState` 以更新 UI。

通过使用 `CupertinoSliverRefreshControl`,你可以在 Flutter 应用中轻松实现原生 iOS 风格的下拉刷新效果。

CustomScrollView

`CustomScrollView` 是 Flutter 提供的一个强大的滚动视图组件,它允许你创建一个包含多个不同类型的滚动子项(如列表、网格、头部等)的可滚动区域。通过使用 `CustomScrollView`,你可以实现复杂的、灵活的滚动布局,这在需要自定义滚动行为和组合多种子组件时非常有用。

基本概念
  • Slivers:`CustomScrollView` 的子组件是 "slivers",它们是可以在滚动视图中动态缩放和展开的组件。常用的 slivers 有 `SliverList`、`SliverGrid`、`SliverAppBar` 等。
  • 灵活性:允许将不同类型的子组件组合在一起,支持复杂的滚动效果和布局。

常用的 Sliver 组件

1.`SliverAppBar`:
  • 支持可折叠的应用栏,可以在用户向下滚动时隐藏,向上滚动时显示。
  • 可以用来创建带有背景图片的动态头部。
2.`SliverList`:
  • 用于创建一个线性列表,类似于 `ListView`。
  • 通过 `SliverChildBuilderDelegate` 或 `SliverChildListDelegate` 来定义列表项。
3.`SliverGrid`:
  • 用于创建网格布局,类似于 `GridView`。
  • 可以通过 `SliverGridDelegate` 控制网格的布局样式。
4.`SliverToBoxAdapter`:
  • 用于将普通的 `Box` 组件(如 `Container`、`SizedBox`)包装成 sliver,方便在 `CustomScrollView` 中使用。
主要属性
`scrollDirection`:
  • 控制滚动的方向,默认为垂直方向(`Axis.vertical`)。
`reverse`:
  • 如果为 `true`,滚动视图的方向将被反转。
`controller`:
  • 可以设置一个 `ScrollController` 来控制滚动位置和监听滚动事件。
`physics`:
  • 控制滚动视图的物理特性,如弹性、阻尼等。常用的有 `BouncingScrollPhysics`、`ClampingScrollPhysics`。
'slivers':

允许开发者将多个 sliver 组件组合在一起,从而实现复杂的滚动布局。定义 `CustomScrollView` 的子组件,这些子组件可以是任何类型的 sliver。

常见的 Sliver 组件
1.`SliverAppBar`:
  • 用于创建可折叠的应用栏,支持滚动时的动态效果(如背景图片缩放)。
  • 可以设置为固定、浮动或完全可折叠。
2.`SliverList`:
  • 用于创建一个线性列表,类似于 `ListView`。
  • 可以通过 `SliverChildBuilderDelegate` 或 `SliverChildListDelegate` 动态或静态地生成子项。
3.`SliverGrid`:
  • 用于创建网格布局,类似于 `GridView`。
  • 通过 `SliverGridDelegate` 控制网格的布局样式。
4.`SliverToBoxAdapter`:
  • 用于将一个普通的 `Box` 组件包装成 sliver,允许在 sliver 列表中使用非 sliver 组件。
5.`SliverPadding`:
  • 用于在 sliver 组件周围添加内边距。
Dart 复制代码
scrollController.position.pixels > 0 &&
                _scrollController.position.pixels ==
                    _scrollController.position.maxScrollExtent

上面代码是用于检测用户是否已经滚动到 `CustomScrollView` 或其他可滚动视图的底部。

`_scrollController.position.pixels`:
  • 这是 `ScrollController` 的一个属性,代表当前滚动视图顶部相对于其内容的滚动偏移量,单位为像素。
  • 当用户向下滚动时,这个值会增加。
`_scrollController.position.maxScrollExtent`:
  • 这是滚动视图内容的最大滚动范围,即内容的总高度减去视图的高度。
  • 当滚动视图滚动到最底部时,`pixels` 的值会等于 `maxScrollExtent`。
逻辑条件:
  • `(_scrollController.position.pixels > 0)`: 这保证了滚动视图至少已经向下滚动了一点(即不在顶部)。
  • `(_scrollController.position.pixels == _scrollController.position.maxScrollExtent)`: 这表示滚动视图已经滚动到了底部。

上面代码通常用于实现上拉加载更多数据的功能。当用户滚动到列表底部时,你可以通过这个条件来触发加载更多数据的操作。具体实现中,通常会在 `NotificationListener` 中监听 `ScrollNotification`,并在满足条件时调用加载方法。

NotificationListener

`NotificationListener` 是 Flutter 中用于监听树状结构中各种通知事件的一个组件。它可以监听从子树中冒泡上来的通知,并在检测到特定类型的通知时执行回调函数。

基本概念
  • 通知机制:Flutter 中的通知是一种在子树中传递信息的机制,通常用于处理全局事件或状态变化。
  • 冒泡事件:通知会从发送它的子组件开始向上冒泡,通过组件树传递直到被捕获或到达根节点。
常见用途
  • 滚动事件监听:最常见的用途之一是监听滚动事件,例如检测用户滚动到列表底部以加载更多数据。
  • 尺寸变化:监听某些组件的尺寸或布局变化通知。
  • 自定义通知:开发者可以定义自己的通知类型以便在组件树中传递自定义事件。
主要属性
`onNotification`:
  • 类型为 `bool Function(T notification)` 的回调函数。
  • 在通知被捕获时调用,接收一个通知对象作为参数。
  • 返回值决定了通知是否继续向上冒泡。返回 `true` 则通知停止向上冒泡,返回 `false` 则继续。
`child`:
  • `NotificationListener` 的子组件。
  • 通常是一个包含滚动组件或其他可能发出通知的组件。
关键点
  • 类型参数 `T`:`NotificationListener` 是一个泛型组件,可以指定要监听的通知类型。例如,`NotificationListener` 监听滚动通知。
  • 过滤通知:通过指定泛型参数,只监听特定类型的通知,其他类型的通知将被忽略。
  • 事件处理:在 `onNotification` 回调中处理捕获的事件,决定是否阻止通知继续冒泡。
注意事项
  • 确保返回值的正确性:返回 `true` 会阻止通知继续传播,通常需要谨慎使用。
  • `NotificationListener` 可以嵌套使用,在不同层级捕获并处理不同的通知。
  • 如果需要自定义通知,可以继承 `Notification` 类,定义自己的通知类型并在合适的地方发送。

SliverSafeArea

`SliverSafeArea` 是 Flutter 提供的一个用于在 `CustomScrollView` 中处理屏幕安全区域的组件。它的作用与 `SafeArea` 类似,但专门用于 sliver 系统,以确保在具有如刘海屏或圆角屏幕的设备上,内容不会被遮挡。

基本概念
  • 安全区域:指的是设备的可视区域,排除了可能遮挡内容的部分,如状态栏、导航栏、底部操作栏(如 iPhone X 的 Home Indicator 区域)。
  • Slivers:`SliverSafeArea` 是专为 sliver 系统设计的安全区域处理器,适用于 `CustomScrollView` 等使用 sliver 构建的滚动视图。
主要属性
`sliver`:
  • `SliverSafeArea` 的子组件,通常是其他 sliver 组件,如 `SliverList`、`SliverGrid` 等。
  • `left`、`top`、`right`、`bottom`:
  • 布尔值,默认为 `true`,用于指定是否在相应的边缘应用安全区域补白。
  • 例如,`top: false` 将不会在顶部应用安全区域补白。
`minimum`:
  • 设置每个边缘的最小补白,通过 `EdgeInsets` 指定。
  • 即使设备没有安全区域,这些最小补白也会被应用。
`maintainBottomViewPadding`:
  • 默认为 `false`。
  • 如果为 `true`,则当键盘出现时,仍然保持底部的安全区域补白。
使用场景
  • 刘海屏处理:在刘海屏设备上,确保内容不被屏幕的非矩形部分遮挡。
  • 统一样式:在不同设备上保持一致的内容展示效果,避免被状态栏或其他系统 UI 遮挡。
  • 滚动视图:在 `CustomScrollView` 中使用 slivers 时,确保应用安全区域的最佳方式。
注意事项
  • `SliverSafeArea` 是专为 `CustomScrollView` 和 sliver 体系设计的,使用时请确保其子组件也是 sliver。
  • 对于非 sliver 的视图,可以使用 `SafeArea` 来实现类似的功能。
  • 在设计布局时考虑不同设备的安全区域差异,确保用户体验一致。

SliverList

`SliverList` 是 Flutter 中用于在 `CustomScrollView` 中创建线性列表的一种 sliver 组件。它的功能类似于 `ListView`,但更加灵活,适合在需要更多自定义滚动效果的场景中使用。

基本概念
  • Slivers:在 Flutter 中,sliver 是一种可以在滚动视图中动态缩放和展开的组件。`SliverList` 是其中一种,用于创建可滚动的线性列表。
  • 自定义滚动视图:`SliverList` 通常与 `CustomScrollView` 搭配使用,以实现复杂的滚动效果和布局。
主要属性
`delegate`:
  • `SliverChildDelegate` 的一个实例,用来提供 `SliverList` 的子项。
有两种常用的子类:
  • `SliverChildBuilderDelegate`:适用于具有大量或动态生成的子项,通过一个回调函数构建子项。
  • `SliverChildListDelegate`:适用于数量固定且已知的子项,通过一个固定列表提供子项。
工作原理
  • 动态构建:`SliverChildBuilderDelegate` 按需创建子项。只有当子项需要显示时,它们才会被构建,适合处理大数据列表。
  • 固定列表:`SliverChildListDelegate` 适合处理小而固定的列表,因为所有子项会在初始化时被创建。
使用场景
  • 自定义滚动效果:用于需要实现特殊滚动效果的场景,如滚动头部、分页加载等。
  • 大数据列表:与 `SliverChildBuilderDelegate` 结合使用以处理大数据集。
  • 组合布局:可以与其他 sliver 一起使用,实现复杂的 UI 组合,如合并列表、网格、固定头部等。
注意事项
  • 性能:`SliverChildBuilderDelegate` 提供的懒加载机制可以显著提高性能,特别是在处理大数据集时。
  • 可扩展性:`SliverList` 可以与其他 sliver 组件(如 `SliverGrid`、`SliverAppBar`)混合使用,实现多样化的布局。
  • 布局限制:`SliverList` 必须在 `CustomScrollView` 或其他支持 sliver 的组件中使用。

通过利用 `SliverList`,开发者可以在 Flutter 中创建灵活且高性能的滚动列表,特别是在需要自定义滚动行为和布局的场景中。

SliverChildBuilderDelegate

`SliverChildBuilderDelegate` 是 Flutter 中用于动态构建 sliver 子组件的一个委托类。它通常与 `SliverList` 或 `SliverGrid` 一起使用,提供一种懒加载的方式来创建子组件,适合处理大数据列表或动态生成的子项。

基本概念
  • 懒加载:`SliverChildBuilderDelegate` 不会一次性构建所有子项,而是根据需要动态创建。当用户滚动到需要显示新子项时,才会调用构建函数生成子项。
  • 高效:这种方式减少了内存消耗和构建开销,特别是在处理大量子项时,这种高效的机制可以显著提高性能。
主要属性
`builder`:
  • 类型为 `IndexedWidgetBuilder`,一个回调函数,用于构建每个子项。
  • 函数签名为 `(BuildContext context, int index)`,其中 `index` 是当前构建的子项索引。
`childCount`:
  • 指定总共有多少个子项。
  • 如果为 `null`,表示子项数量不受限制,但 `builder` 必须处理 `index` 超过数据范围的情况。
`findChildIndexCallback`:
  • 类型为 `ChildIndexGetter`,用于在需要时查找指定 key 的索引。
  • 这在需要保持子项状态或支持滚动到特定项时非常有用。
工作原理
  • 动态生成:当 `CustomScrollView` 需要显示新子项时,`SliverChildBuilderDelegate` 调用 `builder` 函数生成子项。
  • 按需加载:仅在用户滚动到相应位置时才创建子项,从而优化了性能和内存使用。
使用场景
  • 大数据列表:特别适用于需要展示大量数据的情况,如长列表。
  • 动态内容:适合用于数据源动态变化的场景,比如从网络加载的内容。
  • 高性能要求:在需要高性能的应用中,使用 `SliverChildBuilderDelegate` 可以显著减少不必要的资源消耗。
注意事项
  • 确保 `builder` 函数能够高效地构建子项。
  • 当 `childCount` 为 `null` 时,`builder` 应该能够处理 `index` 超出范围的情况(例如返回 `null`)。
  • 如果需要在子项之间共享状态或保持状态,请确保正确地管理状态,可能需要使用 `Key` 或其他

BouncingScrollPhysics

`BouncingScrollPhysics` 是 Flutter 中的一种滚动物理效果,用于定义滚动视图在用户滚动到边界时的反弹行为。它模仿了 iOS 上的滚动惯性和边界反弹效果,这种效果在 iOS 应用中非常常见。

基本概念
  • 滚动物理:滚动物理类定义了滚动行为的物理特性,如惯性、阻尼和边界行为。
  • 边界反弹:`BouncingScrollPhysics` 特别适用于需要在内容到达边界时显示反弹效果的场景,用户体验类似于 iOS 的滚动视图。
主要特点
  • 反弹效果:当用户滚动超出内容边界时,内容会有一个自然的反弹效果,给人一种弹性和流畅的感觉。
  • 惯性滚动:滚动停止时的惯性行为模拟真实世界的物理效果,使得滚动体验更加自然。
主要属性
parent`:
  • `ScrollPhysics` 的父级设置,如果有的话,允许组合不同的滚动物理效果。
  • 例如,可以将 `BouncingScrollPhysics` 与 `AlwaysScrollableScrollPhysics` 结合使用,以确保在任何情况下都能滚动。
使用场景
  • iOS 风格应用:特别适合在 iOS 风格的应用中使用,以提供一致的用户体验。
  • 弹性效果:需要在内容边界提供弹性滚动效果的应用场景。
注意事项
  • 平台一致性:默认情况下,Flutter 在 iOS 上会自动使用 `BouncingScrollPhysics`,而在 Android 上使用 `ClampingScrollPhysics`。如果希望在 Android 上也使用弹性效果,需要显式设置 `BouncingScrollPhysics`。
  • 组合使用:可以通过设置 `parent` 属性来组合不同的滚动物理效果,满足复杂的滚动需求。
  • 用户体验:在设计用户体验时,考虑目标平台的用户期望,以选择合适的滚动物理效果。例如,Android 用户通常不期待滚动反弹,因此在 Android 上默认没有这种效果。
相关推荐
LuckyLay1 小时前
Spring学习笔记_34——@Controller
spring·controller
特立独行的猫a18 天前
HarmonyOS NEXT 应用开发实战(八、知乎日报List列表下拉刷新及上滑加载更多分页的实现)
华为·harmonyos·下拉刷新·list组件·上滑加载更多
且听真言3 个月前
Flutter ListView控件
flutter·builder·controller·listview·listview原理
醉颜凉3 个月前
SpringMVC 运行流程
java·面试·mvc·springmvc·model·controller·view
modelsetget4 个月前
非Controller控制层参数校验怎么办
spring·controller·参数校检·参数验证
G皮T5 个月前
【单元测试】Controller、Service、Repository 层的单元测试
单元测试·service·controller·repository
fatfishccc5 个月前
SpringMVC
mvc·springmvc·restful·mvvm·过滤器·controller·拦截器
景天科技苑5 个月前
【微信小程序开发】小程序中的上滑加载更多,下拉刷新是如何实现的?
微信小程序·小程序·下拉刷新·上滑加载更多
放羊的牧码7 个月前
Kafka - Kafka 为啥抛弃 Zookeeper?
分布式·zookeeper·kafka·controller·broker·zk·为啥抛弃