方式一:RefreshIndicator+ListView实现
import 'package:flutter/material.dart';
class SimpleRefreshDemoPage extends StatefulWidget {
const SimpleRefreshDemoPage({super.key});
@override
State createState() {
return _SimpleRefreshDemoPage();
}
}
class _SimpleRefreshDemoPage extends State {
final int pageSize = 50;
bool disposed = false;
List dataList = [];
final ScrollController _scrollController = ScrollController();
final GlobalKey refreshKey = GlobalKey();
Future onRefresh() async {
await Future.delayed(const Duration(seconds: 4));
dataList.clear();
for (int i = 0; i < pageSize; i++) {
dataList.add("refresh");
}
if (disposed) {
return;
}
setState(() {});
}
Future loadMore() async {
await Future.delayed(const Duration(seconds: 4));
for (int i = 0; i < pageSize; i++) {
dataList.add("loadmore");
}
if (disposed) {
return;
}
setState(() {});
}
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
loadMore();
}
});
Future.delayed(const Duration(seconds: 0), () {
refreshKey.currentState?.show();
});
}
@override
void dispose() {
disposed = true;
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("SimpleRefreshDemoPage"),
),
body: RefreshIndicator(
key: refreshKey,
onRefresh: onRefresh,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, 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"),
),
);
},
itemCount: (dataList.length >= pageSize)
? dataList.length + 1
: dataList.length,
controller: _scrollController,
),
),
);
}
}
如何实现下拉刷新
RefreshIndicator
RefreshIndicator 是 Flutter 中用于实现下拉刷新功能的一个组件。RefreshIndicator 包裹一个可滚动的子组件,如 ListView
或 GridView
,当用户下拉到列表顶部时,会触发刷新操作。上面的例子中RefreshIndicator包裹在了ListView。
`onRefresh` 是一个返回 `Future` 的异步函数,用于执行刷新操作。
关键属性
onRefresh: 必须实现的回调函数,定义刷新时的操作。
child: 需要包裹的可滚动子组件。
color: 刷新指示器的进度条颜色。
backgroundColor: 刷新指示器的背景色。
displacement: 指示器开始显示时与顶部的距离。 如何实现下拉加载 ScrollController _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { loadMore(); } }); 如何检测用户是否滚动到了
ScrollView的底部? _scrollController.position.pixels == _scrollController.position.maxScrollExtent _scrollController.position.pixels
:表示当前滚动的位置。
_scrollController.position.maxScrollExtent
:表示可滚动区域的最大值。
上面两个值相等时,说明用户已经滚动到了底部。
bool disposed
字段作用
在上面的代码中,bool disposed
字段用于指示 State
对象是否已经被销毁。当 dispose
方法被调用时,disposed
被设置为 true
。这个字段主要用于在异步操作完成后,确保不会调用已经被销毁的 State
对象的 setState
方法。
作用分析
1.防止在销毁后调用 setState
:
异步操作(如 Future.delayed
)在完成后,可能会尝试调用 setState
来更新 UI。然而,如果在异步操作执行期间,用户已经导航离开了当前页面,导致 State
对象被销毁,那么调用 setState
就会抛出异常,因为 State
已经不再是活动的。
通过检查 disposed
字段,可以在调用 setState
前确认 State
是否仍然有效,从而避免在组件销毁后对其进行不必要的更新。
2.提高代码的稳健性:
这种模式是一种防御性编程的方法,确保应用程序在面对不确定的异步执行时仍然是稳定的。
@override
void dispose() {
disposed = true;
super.dispose();
}
Future onRefresh() async {
await Future.delayed(const Duration(seconds: 4));
dataList.clear();
for (int i = 0; i < pageSize; i++) {
dataList.add("refresh");
}
if (disposed) {
return;
}
setState(() {});
}
Future loadMore() async {
await Future.delayed(const Duration(seconds: 4));
for (int i = 0; i < pageSize; i++) {
dataList.add("loadmore");
}
if (disposed) {
return;
}
setState(() {});
}
在 onRefresh
和 loadMore
方法中,disposed
被用来检查 State
是否已经被销毁。如果 disposed
为 true
,则不再调用 setState
,从而避免可能的异常。
3.总结
使用 disposed
字段可以有效地防止在组件销毁后进行不必要或有害的 UI 更新。这种模式特别适用于涉及异步操作的 Flutter 应用开发,确保代码在处理 State
生命周期时更加健壮。