Flutter - 别手抖!pd_cooldown 给按钮加冷却,给后端放假

65 阅读5分钟

pd_cooldown:别急,给操作一点「冷静期」

一句话简介:这是一个专业的 Flutter 防抖(Debounce)与节流(Throttle)插件,用一个“冷却器”为你的按钮和接口降温,杜绝“手抖连击”和“猛戳服务器”。

  • 插件主页:pub.dev/packages/pd…
  • 适用场景:表单提交防重复、搜索框输入防抖、按钮点击节流、接口保护与资源限流。

为什么需要「冷却期」?

  • 用户手速过快,后端心率过高:多次请求可能导致重复下单、重复扣费、资源占用暴增。
  • UI“瞬间爽”,业务“后劲大”:连击交互体验好,但代价是排队、回滚与脏数据。
  • 你需要一个“靠谱的保镖”,在窗口时间里只让一次操作通过。

pd_cooldown 的核心就是:在指定的冷却窗口内,第一次触发执行;其后触发被优雅拒绝或延迟,直到时间窗结束。


安装与引入

  • pubspec.yaml 中添加依赖:
dependencies:
  pd_cooldown: ^0.2.1
  • 安装依赖:
flutter pub get
  • 引入库:
import 'package:pd_cooldown/pd_cooldown.dart';

开箱即用:统一“冷却执行器”

用一个对象守住你的操作,重复触发也不慌。

// 创建防抖/节流统一执行器(默认带日志和错误处理)
final cooldown = PDCooldown.withDefaults(
  cooldownDuration: const Duration(seconds: 3),
);

// 在冷却窗口内,仅第一次执行;其后触发会被优雅拒绝(返回 null)
final result = await cooldown.execute<String>(() async {
  // 你的异步操作(例如网络请求)
  await Future.delayed(const Duration(milliseconds: 500));
  return '操作完成';
}, onCooldown: (remaining) {
  // 告知用户还需等待多久(可接入 Toast/Tip)
  print('冷却中,剩余时间: ${remaining.inSeconds}秒');
});

if (result != null) {
  print('执行结果: $result');
} else {
  print('操作被冷却限制');
}

常用方法

  • execute<T>():执行带冷却保护的操作。
  • isInCooldown():是否处于冷却中。
  • getRemainingCooldown():获取剩余冷却时间。
  • reset():重置冷却(如用户取消、需重新尝试时)。

可定制构造

final cooldown = PDCooldown.withCallbacks(
  cooldownDuration: const Duration(seconds: 5),
  onDebug: (message) => print('调试: $message'),
  onError: (message) => print('错误: $message'),
  onErrorHandler: (error, stackTrace, prefix) {
    // 自定义错误处理逻辑(可接入日志/埋点)
    print('$prefix: $error');
  },
);

不写业务也能用:内置 UI 组件

直接把按钮换成“带冷却”的版本,连击自然没戏。

节流按钮 PDThrottleButton

  • 语义:第一次点击立刻响应,冷却期间忽略后续点击。
PDThrottleButton(
  buttonType: PDButtonType.elevated,
  debounceDuration: const Duration(seconds: 2),
  onPressed: () {
    print('节流按钮被点击');
  },
  child: const Text('节流按钮'),
)

防抖按钮 PDDebounceButton

  • 语义:延迟响应,只有在指定时间内没有新点击时才触发。
PDDebounceButton(
  buttonType: PDButtonType.elevated,
  debounceDuration: const Duration(milliseconds: 800),
  onPressed: () {
    print('防抖按钮被点击');
  },
  child: const Text('防抖按钮'),
)

常用属性

  • onPressed:点击回调。
  • child:子组件。
  • debounceDuration:时间窗口(名称统一,节流/防抖皆用)。
  • buttonType:按钮类型。
  • cooldownOpacity/debounceOpacity:冷却或防抖期间透明度(视觉弱化,提示“还在冷却”)。

常见场景与配方

  • 表单提交:节流 3–5 秒,禁止重复下单/扣费;按钮半透明+提示文案。
  • 搜索框输入:防抖 300–800 ms,只有“停下来的那次”才请求后端。
  • 点赞/收藏:节流 1–2 秒,避免“激情连点”。
  • 刷新列表/切换页签:节流 1 秒,避免重复构建与闪烁。
  • 上传/支付等高风险操作:节流 5–10 秒,搭配可视冷却反馈。

最佳实践(把冷却用得更优雅)

  • 语义分工:
    • “节流”:立刻执行一次,其后忽略;适合点击型动作。
    • “防抖”:等待稳定后执行;适合输入与搜索。
  • 时间窗选择:
    • 交互操作:200–800 ms 防抖。
    • 网络提交:2–5 s 节流。
    • 高风险动作:5–10 s 节流并可视提示。
  • 统一封装:将业务常见冷却封装成服务(如 SubmitCooldownService),团队统一调用,避免重复造轮子。
  • 可观测性:打通 onDebug/onError 到日志系统(Sentry/自研 Logger),便于观察“热手”用户与异常触发。
  • UI 反馈:冷却期按钮半透明或禁用;搭配 Loading/Tip 提示“我在工作”。

进阶技巧(让冷却更聪明)

  • 多实例分域:搜索与提交各用独立 PDCooldown,互不干扰,避免“误共享”。
  • 非阻塞语义:execute<T> 返回 null 即表示被冷却拒绝;根据此分支设计 UX(如弹出“稍等片刻”)。
  • 灵活重置:用户主动取消或失败重试时 reset();严谨控制生命周期,避免“冷却幽灵”。
  • 状态管理协作:在 Provider/Bloc/Riverpod 中持有 PDCooldown 对象,清晰管理释放与复用。

常见问答(FAQ)

  • Q:透明度降低,用户会以为按钮坏了?
    • A:这正是提示“冷却中”的信号。可搭配文字或动画提升感知。
  • Q:这和普通 debounce/throttle 库有什么不同?
    • A:pd_cooldown 提供统一执行器与现成按钮组件,API 简洁、语义明确,并支持调试与错误回调,拿来即用。
  • Q:能否并发多次执行?
    • A:设计语义是“窗口内只执行一次”,其余拒绝;同步锁保证线程安全,不建议并发破坏语义。
  • Q:如何给后端“休息权”?
    • A:对可能导致重复下单/扣费的操作,节流 3–5 秒并加不可重复提示,保护服务与用户体验。
  • Q:我就想疯狂点,咋办?
    • A:我们支持冷静;你的后端更支持午睡。请理性连击。

小结与彩蛋

  • 一句话:把“手速竞赛”变成“优雅绅士”,交互顺滑,服务稳健。
  • 彩蛋:给“提交按钮”配上节流 + 半透明 + 提示文案——你的后端同事会请你喝奶茶。

参考资料