Flutter下拉刷新上拉加载的简单实现方式二
一个简单的Flutter应用程序,展示了如何实现下拉刷新和上拉加载更多的功能。
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 组件周围添加内边距。
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 上默认没有这种效果。