更新:
XBRefresh
安装
flutter pub add xb_refresh
使用
// ignore_for_file: library_private_types_in_public_api
import 'package:xb_scaffold/xb_scaffold.dart';
import 'xb_refresh.dart';
class XBRefreshDemo extends XBPage<XBRefreshDemoVM> {
const XBRefreshDemo({super.key});
@override
generateVM(BuildContext context) {
return XBRefreshDemoVM(context: context);
}
@override
bool needShowContentFromScreenTop(XBRefreshDemoVM vm) {
return true;
}
@override
List<Widget>? actions(XBRefreshDemoVM vm) {
return [
XBButton(
onTap: () {
vm.xbRefreshController.refresh();
},
child: Container(
color: Colors.transparent,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text("开始刷新"),
),
)),
];
}
@override
Widget buildPage(vm, BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: topBarH),
child: Container(
height: screenH * 0.8,
width: screenW * 0.8,
color: colors.randColor,
child: XBRefresh(
controller: vm.xbRefreshController,
needLoadMore: true,
needRefresh: true,
initRefresh: true,
///开始加载更多的回调
onLoadMore: () {
Future.delayed(const Duration(seconds: 2), () {
bool hasMore = false;
if (vm.itemCount < 20) {
hasMore = true;
vm.itemCount += 2;
vm.notify();
}
///结束加载更多,传是否有新数据
vm.xbRefreshController.endLoadMore(hasMore);
});
},
onRefresh: () {
Future.delayed(const Duration(seconds: 1), () {
vm.itemCount = 10;
vm.xbRefreshController.endRefresh();
vm.notify();
});
},
headerCompleteBuilder: (height) {
return Container(
height: height,
color: Colors.red,
child: const Center(
child: Text("完成刷新"),
),
);
},
footerHasMoreBuilder: (height) {
return Container(
height: height,
color: Colors.green,
child: const Center(
child: Text("拉取新数据完成"),
),
);
},
child: ListView.builder(
itemCount: vm.itemCount,
itemBuilder: (context, index) {
return Cell("$index", () {});
},
),
),
),
);
}
}
class XBRefreshDemoVM extends XBPageVM<XBRefreshDemo> {
XBRefreshDemoVM({required super.context}) {
controller.addListener(listenFun);
}
final ScrollController controller = ScrollController();
final XBRefreshController xbRefreshController = XBRefreshController();
int itemCount = 10;
void listenFun() {
xbError("controller.offset:${controller.offset}");
}
@override
void dispose() {
controller.removeListener(listenFun);
controller.dispose();
super.dispose();
}
}
class Cell extends StatelessWidget {
static const height = 44.0;
final String title;
final VoidCallback onPressed;
const Cell(this.title, this.onPressed, {super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
height: height,
color: Colors.black38,
alignment: Alignment.center,
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: Text(title,
style: const TextStyle(color: Colors.white)))),
Container(
height: 1,
color: Colors.white,
)
],
),
),
);
}
}
原文
1,通用性,child可以是任何widget
2,支持多种状态:
上拉加载更多:继续上拉加载更多、松手开始加载、正在加载、加载到了新数据、没有新数据
下拉刷新:继续下拉刷新、松手开始刷新、正在刷新、刷新完成
3,支持自定义每种状态的widget
源码:
效果:
思路:
下拉和上拉类似的,这里就说上拉。
借住Stack,底层是Column,上层是外部传入的child。
流程图(仅上拉):
demo:
下载源码后,跳转到下图指向的page查看效果。
class _XBRefreshDemoState extends State<XBRefreshDemo> {
ScrollController _controller = ScrollController();
GlobalKey<XBRefreshState> _refreshKey = GlobalKey();
int _itemCount = 10;
@override
void initState() {
// TODO: implement initState
super.initState();
_controller.addListener(() {
_refreshKey.currentState.receiveOffset(
_controller.offset, _controller.position.maxScrollExtent);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("xb refresh demo"),
),
body: XBRefresh(
key: _refreshKey,
needLoadMore: true,
needRefresh: true,
///开始加载更多的回调
onBeginLoadMore: () {
Future.delayed(Duration(seconds: 2), () {
bool hasMore = false;
if (_itemCount < 20) {
hasMore = true;
setState(() {
_itemCount += 5;
});
}
///结束加载更多,传是否有新数据
_refreshKey.currentState.endLoadMore(hasMore);
});
},
onBeginRefresh: () {
Future.delayed(Duration(seconds: 1), () {
setState(() {
_itemCount = 10;
});
_refreshKey.currentState.endRefresh();
});
},
child: CustomScrollView(
controller: _controller,
physics: AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics()),
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate((ctx, index) {
return Cell("$index", () {});
}, childCount: _itemCount))
],
)));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}