Flutter下拉刷新功能插件pull_to_refresh_new的使用
Flutter 下拉刷新功能插件 pull_to_refresh_new
的使用
简介
pull_to_refresh_new
是一个为 Flutter 滚动组件提供下拉刷新和上拉加载更多功能的插件。它支持 Android、iOS 和 Web 平台。如果你是中文用户,可以点击这里查看 中文文档。
特性
- 支持 Android、iOS 和 Web 的刷新指示器组件
- 支持上拉加载更多和下拉刷新
- 几乎适用于所有滚动组件,如
GridView
、ListView
等 - 提供全局设置默认指示器和属性
- 提供一些常用的指示器
- 支持 Android 和 iOS 默认的滚动物理效果,可以控制过度滚动距离,自定义弹簧动画、阻尼、速度
- 支持水平和垂直刷新,支持反向滚动视图(四个方向)
- 提供多种刷新样式:Behind、Follow、UnFollow、Front,提供多种加载更多样式
- 支持两级刷新,实现类似淘宝两级刷新、微信两级刷新
- 支持将指示器放置在其他位置,实现类似微信朋友圈的刷新效果
使用方法
添加依赖
在 pubspec.yaml
文件中添加以下依赖:
dependencies:
pull_to_refresh_new: ^2.0.5
导入包
在 Dart 文件中导入 pull_to_refresh_new
包:
import 'package:pull_to_refresh_new/pull_to_refresh_new.dart';
简单示例
以下是一个简单的示例,展示了如何使用 SmartRefresher
实现下拉刷新和上拉加载更多功能:
import 'package:flutter/material.dart';
import 'package:pull_to_refresh_new/pull_to_refresh_new.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> items = ["1", "2", "3", "4", "5", "6", "7", "8"];
RefreshController _refreshController = RefreshController(initialRefresh: false);
void _onRefresh() async {
// 监控网络请求
await Future.delayed(Duration(milliseconds: 1000));
// 如果失败,使用 refreshFailed()
_refreshController.refreshCompleted();
}
void _onLoading() async {
// 监控网络请求
await Future.delayed(Duration(milliseconds: 1000));
// 如果失败,使用 loadFailed(),如果没有数据返回,使用 LoadNodata()
items.add((items.length + 1).toString());
if (mounted) {
setState(() {});
}
_refreshController.loadComplete();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pull to Refresh Example'),
),
body: SmartRefresher(
enablePullDown: true,
enablePullUp: true,
header: WaterDropHeader(),
footer: CustomFooter(
builder: (BuildContext context, LoadStatus? mode) {
Widget body;
if (mode == LoadStatus.idle) {
body = Text("pull up load");
} else if (mode == LoadStatus.loading) {
body = CupertinoActivityIndicator();
} else if (mode == LoadStatus.failed) {
body = Text("Load Failed!Click retry!");
} else if (mode == LoadStatus.canLoading) {
body = Text("release to load more");
} else {
body = Text("No more Data");
}
return Container(
height: 55.0,
child: Center(child: body),
);
},
),
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
child: ListView.builder(
itemBuilder: (context, index) => Card(child: Center(child: Text(items[index]))),
itemExtent: 100.0,
itemCount: items.length,
),
),
);
}
@override
void dispose() {
_refreshController.dispose();
super.dispose();
}
}
全局配置
RefreshConfiguration
可以配置所有子树下的 SmartRefresher
表现,通常存储在 MaterialApp
的根部,用法类似于 ScrollConfiguration
。如果其中一个 SmartRefresher
需要与其他不同,可以使用 RefreshConfiguration.copyAncestor()
复制祖先的属性并替换非空属性。
RefreshConfiguration(
headerBuilder: () => WaterDropHeader(), // 配置默认头部指示器
footerBuilder: () => ClassicFooter(), // 配置默认底部指示器
headerTriggerDistance: 80.0, // 头部触发刷新的距离
springDescription: SpringDescription(stiffness: 170, damping: 16, mass: 1.9), // 自定义回弹动画
maxOverScrollExtent: 100, // 头部最大拖拽范围
maxUnderScrollExtent: 0, // 底部最大拖拽范围
enableScrollWhenRefreshCompleted: true, // 刷新完成后是否允许滚动
enableLoadingWhenFailed: true, // 加载失败时是否允许手势上拉加载更多
hideFooterWhenNotFull: false, // 视图小于一屏时是否禁用上拉加载更多
enableBallisticLoad: true, // 是否通过 BallisticScrollActivity 触发加载更多
child: MaterialApp(
// ...
),
);
国际化
从 1.5.6 版本开始,增加了国际化支持,可以在 MaterialApp
或 CupertinoApp
中添加以下代码:
MaterialApp(
localizationsDelegates: [
RefreshLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
const Locale('zh'),
],
localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) {
return locale;
},
// ...
);
屏幕截图
示例
风格 | 基础 | 头部在其他位置 | 反向 + 水平 |
---|---|---|---|
图片 |
风格 | 两级刷新 | 与其他组件结合 | 聊天 |
---|---|---|---|
图片 |
风格 | 简单自定义头部 | 可拖动底部加载更多 | GIF 指示器 |
---|---|---|---|
图片 |
指示器
刷新样式 | 加载更多样式 | ||
---|---|---|---|
Follow | ShowAlways | ||
UnFollow | HideAlways | ||
Behind | ShowWhenLoading | ||
Front |
风格 | ClassicIndicator | WaterDropHeader | MaterialClassicHeader |
---|---|---|---|
图片 |
风格 | WaterDropMaterialHeader | Shimmer Indicator | Bezier+Circle |
---|---|---|---|
图片 |
关于 SmartRefresher
的子组件说明
自 1.4.3 版本起,child
属性从 ScrollView
变为 Widget
,但这并不意味着所有组件都处理相同。SmartRefresher
的内部实现机制与 NestedScrollView
不同。
主要有两种处理机制:
- 继承自
ScrollView
的组件,目前有三种类型:ListView
、GridView
、CustomScrollView
。 - 不继承自
ScrollView
的组件,一般用于放置空视图、不可滚动视图(将不可滚动转换为可滚动)、PageView
等,不需要自己通过LayoutBuilder
估算高度。
对于第一种机制,slivers
会被“非法”地从系统中取出。第二种机制则是将子组件直接放入 SliverToBoxAdapter
类中,通过拼接头部和尾部形成 slivers
,然后将 slivers
放入 SmartRefresher
中的 CustomScrollView
。因此,SmartRefresher
可以理解为 CustomScrollView
,因为其内部返回的是 CustomScrollView
。所以,child
节点和 ScrollView
有很大的区别。
例如,你可能需要在 ScrollView
外部添加背景、滚动条等,以下是错误和正确的做法:
// 错误
SmartRefresher(
child: ScrollBar(
child: ListView(
// ...
),
),
)
// 正确
ScrollBar(
child: SmartRefresher(
child: ListView(
// ...
),
),
)
另一个错误的做法是将 ScrollView
放在另一个组件中:
// 错误
SmartRefresher(
child: MainView(),
)
class MainView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
// ...
);
}
}
上述错误会导致嵌套滚动,导致你无论怎么滑动都看不到头部和尾部。同样,如果你需要与 NotificationListener
、ScrollConfiguration
等组件配合使用,记得不要将它们放在 ScrollView
(你想要添加刷新部分)和 SmartRefresher
之外。
更多资源
存在的问题
- 关于
NestedScrollView
,当你快速下滑再上滑时,会回到原来的位置。主要原因是NestedScrollView
没有考虑bouncingScrollPhysics
下的跨边界弹性问题。相关 Flutter 问题:34316, 33367, 29264。这个问题只能等待 Flutter 修复。 SmartRefresher
无法将刷新注入到子树下的ScrollView
中,即如果你将AnimatedList
或RecordableListView
放在child
中是不可能的。我尝试了多种解决方法但都失败了。由于实现原理,我不得不将它们附加到slivers
的头部和尾部。实际上,这个问题并不是我的组件问题,如AnimatedList
无法与AnimatedList
和GridView
一起使用,除非将AnimatedList
转换为SliverAnimatedList
。目前,我有一个临时解决方案,但需要重写内部代码并在外部ScrollView
中添加SmartRefresher
,参见我的两个示例 示例 1 和 示例 2。
致谢
感谢 SmartRefreshLayout 的启发。
许可证
MIT License
Copyright © 2023 suoshuaihong
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
更多关于Flutter下拉刷新功能插件pull_to_refresh_new的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter下拉刷新功能插件pull_to_refresh_new的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用pull_to_refresh_new
插件来实现下拉刷新功能的代码示例。这个插件提供了丰富的刷新效果和配置选项,可以满足不同的需求。
首先,你需要在pubspec.yaml
文件中添加该插件的依赖:
dependencies:
flutter:
sdk: flutter
pull_to_refresh_new: ^0.2.0 # 请注意版本号,根据最新版本进行替换
然后,运行flutter pub get
来安装依赖。
接下来,在你的Flutter项目中,你可以按照以下方式使用pull_to_refresh_new
插件:
import 'package:flutter/material.dart';
import 'package:pull_to_refresh_new/pull_to_refresh_new.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Pull To Refresh Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final RefreshController _refreshController = RefreshController();
List<String> _items = List<String>.generate(20, (i) => "Item ${i + 1}");
@override
void initState() {
super.initState();
// 监听刷新状态变化
_refreshController.addListener(() {
if (_refreshController.refreshState == RefreshState.Idle) {
// 刷新完成,可以在这里处理一些逻辑
print('Refresh completed.');
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pull To Refresh Example'),
),
body: SmartRefresher(
controller: _refreshController,
enablePullDown: true,
enablePullUp: false,
onRefresh: _onRefresh,
header: WaterDropHeader(), // 可以使用不同的Header,如ClassicHeader, MaterialHeader等
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_items[index]),
);
},
),
),
);
}
Future<void> _onRefresh() async {
// 模拟网络请求
await Future.delayed(Duration(seconds: 2));
// 更新数据
setState(() {
_items = List<String>.generate(20, (i) => "New Item ${i + 1}");
});
// 通知刷新完成
_refreshController.refreshCompleted();
}
@override
void dispose() {
_refreshController.dispose();
super.dispose();
}
}
在这个示例中:
- 我们创建了一个
RefreshController
来控制刷新状态。 - 使用
SmartRefresher
组件包裹了一个ListView
,并设置了刷新时调用的onRefresh
方法和一个自定义的Header(这里使用的是WaterDropHeader
,你也可以选择其他的Header样式)。 - 在
_onRefresh
方法中,我们模拟了一个网络请求,并在请求完成后更新了列表数据,然后调用_refreshController.refreshCompleted()
来通知刷新完成。 - 在
dispose
方法中,我们释放了RefreshController
资源,以避免内存泄漏。
这个示例展示了如何使用pull_to_refresh_new
插件来实现基本的下拉刷新功能。你可以根据需求进一步自定义和扩展这个示例。