Files
huangguo_android/lib/tools_base/refresh/pull_refresh.dart
T
2026-09-15 15:44:13 +07:00

180 lines
6.0 KiB
Dart

import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:hgdj/assets_tool/app_colors.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
// 回弹弹簧:临界阻尼(≈2√(stiffness·mass)=34.6) → 松手快速收回不过冲
// 注:实测此调优手感优于 3.16.8 包默认(mass2.2/stiffness150/damping16,欠阻尼、回弹更夸张),
// 故采用这套调优值,不复刻 3.16.8 原值
// 提成常量供横向的 HorizontalLoadMore 复用,两处手感必须一致,改这里即全改
const SpringDescription kRefreshSpring =
SpringDescription(mass: 1.5, stiffness: 200, damping: 35);
const double kRefreshDragSpeedRatio = 0.91;
Widget pullYsRefresh({
required RefreshViewOnInit onInit,
required Widget child,
Key? key,
final Function(RefreshController? ctr)? onRefresh,
final Function(RefreshController? ctr)? onLoading,
bool enablePullUp = true,
bool enablePullDown = true,
String noDataText = "—— 没有更多数据了 ——",
Widget? customHeader,
Widget? footerHeader,
}) {
return CustomRefreshView(
onInit: onInit,
onLoading: onLoading,
onRefresh: onRefresh,
customHeader: customHeader,
footerHeader: footerHeader,
enablePullDown: enablePullDown,
enablePullUp: enablePullUp,
noDataText: noDataText,
child: child,
);
}
typedef RefreshViewOnInit = Function(RefreshController controller);
class CustomRefreshView extends StatefulWidget {
final bool enablePullDown;
final bool enablePullUp;
final Function(RefreshController? ctr)? onRefresh;
final Function(RefreshController? ctr)? onLoading;
final Widget? customHeader;
final Widget? footerHeader;
final Widget child;
final String noDataText;
final RefreshViewOnInit onInit;
const CustomRefreshView({
super.key,
required this.child,
required this.onInit,
this.enablePullDown = true,
this.enablePullUp = true,
this.onRefresh,
this.onLoading,
this.customHeader,
this.footerHeader,
this.noDataText = "—— 没有更多数据了 ——",
});
@override
State<CustomRefreshView> createState() => _CustomIRefreshViewState();
}
class _CustomIRefreshViewState extends State<CustomRefreshView> {
final refreshCtr = RefreshController();
@override
void initState() {
widget.onInit(refreshCtr);
super.initState();
}
@override
void dispose() {
//组件创建的 controller 由组件释放(谁创建谁释放)
refreshCtr.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RefreshConfiguration(
springDescription: kRefreshSpring,
dragSpeedRatio: kRefreshDragSpeedRatio,
maxOverScrollExtent: 60,
maxUnderScrollExtent: 0,
enableBallisticRefresh: false, // 下拉不靠惯性触发刷新
enableBallisticLoad: true, // 上拉滑到底自动加载
//去掉 StretchingOverscrollIndicator,避免与下拉刷新冲突
child: ScrollConfiguration(
behavior: NoStretchScrollBehavior(),
child: SmartRefresher(
enablePullDown: widget.enablePullDown,
enablePullUp: widget.enablePullUp,
controller: refreshCtr,
onRefresh: () => widget.onRefresh?.call(refreshCtr),
onLoading: () => widget.onLoading?.call(refreshCtr),
header: widget.customHeader ??
WaterDropHeader(
waterDropColor: AppColors.actionRed,
// iOS 刷新菊花用主题色;Android 保持默认 loading
refresh: Platform.isIOS
? const CupertinoActivityIndicator(
color: AppColors.actionRed)
: null,
complete: const Text(
"刷新完成!",
style: TextStyle(
color: AppColors.tipTextColor99, fontSize: 12),
),
failed: const Text(
"刷新失敗!",
style: TextStyle(
color: AppColors.tipTextColor99, fontSize: 12),
),
),
footer: widget.footerHeader ??
ClassicFooter(
loadingText: "加载中...",
canLoadingText: "松开加载更多...",
noDataText: widget.noDataText,
idleText: "上拉加载更多",
// Android 默认转圈是蓝色,改成主题色;iOS 保持菊花
loadingIcon: SizedBox(
width: 25,
height: 25,
child: Platform.isIOS
? const CupertinoActivityIndicator()
: const CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
AlwaysStoppedAnimation(AppColors.actionRed),
),
),
textStyle: TextStyle(
color: AppColors.tipTextColor99,
fontSize: 12,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
child: widget.child),
),
);
}
}
//还原 Flutter 3.16 时代的滚动行为
class NoStretchScrollBehavior extends MaterialScrollBehavior {
//去掉 StretchingOverscrollIndicator,避免与下拉刷新冲突
@override
Widget buildOverscrollIndicator(
BuildContext context,
Widget child,
ScrollableDetails details,
) {
return child;
}
//还原旧版多指拖拽策略(3.19+ 改为 latestPointer,拖拽手感有细微差异)
@override
MultitouchDragStrategy getMultitouchDragStrategy(BuildContext context) {
return MultitouchDragStrategy.averageBoundaryPointers;
}
//使用 BouncingScrollPhysics 还原弹性下拉手感
@override
ScrollPhysics getScrollPhysics(BuildContext context) {
return const BouncingScrollPhysics();
}
}